tags= #r_code #ggplot2 #data_science

library(tidyverse)

Getting and Writing Data

Importing Data

Tab Delimited Text

mydata <- read.delim("my_data.txt", header=TRUE, na.strings="NA")

Importing Multiple CSV files at once

Stack Overflow - Importing Multiple CSV files in R

temp = list.files(pattern="*.csv")
for (i in 1:length(temp)) assign(temp[i], read.csv(temp[i]))   

Writing Out

Write out a tab delimited text

#tab delimitted text file
write.table(my_data, "my_data_output", sep="\t")

Wrangling


Data Frames

Remove Columns

Remove an entire column from a data.frame in R

Remove a column

> Data$genome <- NULL
> head(Data)
   chr region
1 chr1    CDS
2 chr1   exon
3 chr1    CDS
4 chr1   exon
5 chr1    CDS
6 chr1   exon

Remove multiple columns

Data[1:2] <- list(NULL)  # Marek
Data[1:2] <- NULL        # does not work!

Add columns to data frame

Adding a New Column to a Data Frame

df["MY_NEW_COLUMN"] <- NA # That creates the new column named "MY_NEW_COLUMN" filled with "NA"
df[c("E","F","G","H","I")] <- NA #create multiple empty columns

See also Adding and Removing Columns - R Cookbook

Add Group Information to Subjects

When you want to put an observation into a group (e.g Case vs. Control), you need to create a new variable. A simple way to do this is to create a vector(s) that contains the Group members and then use mutate with if_else().

In this example, I created a group called “Domestic” for some cars based on their manufacturer variable.

domestic = c("chevrolet", "dodge", "ford", "jeep", "lincoln", "mercury", "pontiac")
mutate(mpg, Location = if_else(manufacturer %in% domestic, "Domestic", "Foreign"))

Merge Data Frames by Variable

data <- merge(d1, d2, by="Patient.ID")

Create multiple columns with different names and data

Adding multiple columns simultaneously with different values in R -SO

library('tidyverse')

df <- head(cars)

mutate(df, Subject = 'F1', Slide = '1A')

Merge multiple data frames using `merge_all()

reshape::merge_all(your_list_with_dfs, ...)

Factors and Categories

Change Order of Factor Levels

pc$Group <- factor(pc$Group, levels = c('Control','Case'),ordered = TRUE)

Categorizing Continuous Data

Purpose: When you want take numerical values and bin them into categories.

Use cases:

  • I have expression values of ISH puncta inside cells. I want to categorize expression levels into discrete categories (high, medium, low).

Categorize continuous data

With cut

df$category <- cut(df$a, breaks=c(-Inf, 0.5, 0.6, Inf), labels=c("low","middle","high"))

With dplyr

res <- df %>% mutate(category=cut(a, breaks=c(-Inf, 0.5, 0.6, Inf), labels=c("low","middle","high")))

Subsetting

select() from dplyr()

“The select() function can be used to select columns of a data frame that you want to focus on. Often you’ll have a large data frame containing “all” of the data, but any given analysis might only use a subset of variables or observations. The select() function allows you to get the few columns you might need.” - Excerpt From: Roger D. Peng. “R Programming for Data Science.” iBooks.

> names(chicago)[1:3]
[1] "city" "tmpd" "dptp"
> subset <- select(chicago, city:dptp)
> head(subset)
  city tmpd   dptp
1 chic 31.5 31.500
2 chic 33.0 29.875
3 chic 33.0 27.375
4 chic 29.0 28.625
5 chic 32.0 28.875
6 chic 40.0 35.125

Functions

Assign the output of a function to the global environment

You get some output from the function and you want it to be stored in the global environment so it can be used elsewhere.

From SO:

assign() or <<- can both be used.

You could use assign:

assign("v","hi",envir = globalenv())

This requires that you have the name of the target global variable as a string, but it can be easy to do this even with a vector of dozens of such things.

This question discusses the differences between assign and <<-. The chief difference is that assign lets you specify the environment – so it is easy to use it to store data in a non-global but persistent environment so that you could e.g. emulate static variables in R. While it is possible to use assign to modify the global environment, you should be aware that it is seldom a good thing to do so. There is too much of a danger of accidentally overwriting data that you don’t want to have overwritten. Code which makes heavy use of global variables can almost always be refactored into cleaner code which doesn’t. If you need to get a lot of heterogeneous data from a function to the calling environment, the cleanest solution would be to return the needed data in a list.

Describing Data

Summary Statistics

summarize() from dplyr


mpg_summary <- mpg %>%
  group_by(class) %>% # group by grouping variable class
  summarize(hwy_sd = sd(hwy), hwy_mean = mean(hwy)) %>% # find the sd and se of the mean
  mutate(hwy_se = hwy_sd/sqrt(length(mpg$hwy))) # add the standard error of the mean

mpg_summary

summaryBy (Base R)

summaryBy(Age ~ med_year, data = data, FUN = function(x) { c(m = mean(x, na.rm=TRUE), s = sd(x, na.rm=TRUE), n =length(x), r=range(x,na.rm=TRUE)) }

Summarize with psych( ) package

library(psych)
describeBy(my_data, my_data$Group, mat=T)

Counting number of values in a dataframe column

Base R

length(which(data_reshaped$Population=="Vglut1-ChR2"))

Tidyverse way using count() or tally()

tally(x, wt, sort = FALSE)

count(x, ..., wt = NULL, sort = FALSE)

add_tally(x, wt, sort = FALSE)

add_count(x, ..., wt = NULL, sort = FALSE)

Visualize

ggplot2

ggplot2 is a package that is used to make attractive and flexible plots. It’s based on the idea of graphics composed of various layers.

Our new template takes seven parameters, the bracketed words that appear in the template. In practice, you rarely need to supply all seven parameters to make a graph because ggplot2 will provide useful defaults for everything except the data, the mappings, and the geom function.

The seven parameters in the template compose the grammar of graphics, a formal system for building plots. The grammar of graphics is based on the insight that you can uniquely describe any plot as a combination of a dataset, a geom, a set of mappings, a stat, a position adjustment, a coordinate system, and a faceting scheme. (http://r4ds.had.co.nz/data-visualisation.html#first-steps)

ggplot(data = <DATA>) + 
  <GEOM_FUNCTION>(
     mapping = aes(<MAPPINGS>),
     stat = <STAT>, 
     position = <POSITION>
  ) +
  <COORDINATE_FUNCTION> +
  <FACET_FUNCTION>

And from socviz

  • Tell the ggplot() function what our data is.The data = … step.
  • Tell ggplot() what relationships we want to see.The mapping = aes(…) step. For convenience we will put the results of the first two steps in an object called p.
  • Tell ggplot how we want to see the relationships in our data.Choose a geom.
  • Layer on geoms as needed, by adding them to the p object one at a time.
  • Use The scale_, family, labs() and guides() functions. some additional functions to adjust scales, labels, tick marks, titles. We’ll learn more about some of these functions shortly.

Histograms

Basic Histogram (ggplot2)

  • Take the data and add a geom_histogram layer with aes() set to the variable you’re interested in.
ggplot(mtcars) + geom_histogram(aes(mpg), binwidth = 5)

Two histograms overlaid (ggplot2)

  • Take a dataset. Make a histogram, use aes() for the variable of interest. Add another geom_hist() layer doing the same. You can change binwidth and alpha
ggplot(mtcars) + geom_histogram(aes(disp), fill="red", alpha=0.2, binwidth = 100)  + geom_histogram(aes(hp), fill="blue", alpha=0.2, binwidth = 100)

Scatter Plots

#using ggplot2

ggplot(mtcars) + geom_point(aes(x = mpg, y = disp, color= mpg >25))

Mosaic Plot

For Categorical x Categorical Data (Contingency Table)

ggmosiac package


library(ggmosaic)
library(NHANES)
 ggplot(data = NHANES) +
   geom_mosaic(aes(weight = Weight, x = product(SleepHrsNight, AgeDecade), fill=factor(SleepHrsNight)), na.rm=TRUE) +    theme(axis.text.x=element_text(angle=-25, hjust= .1)) + labs(x="Age in Decades ", title='f(SleepHrsNight | AgeDecade) f(AgeDecade)') + guides(fill=guide_legend(title = "SleepHrsNight", reverse = TRUE))

Bar Plots

Basic Bar Plot with Overlaid Points

  • Each geom is a layer. To get the mean of the data_points, no conversion is necessary. You can use the stat=summary argument. Error bars, however, must be calculated and entered into the data.

Plotting means and error bars - R Cookbook


#get summary statistics
mpg_summary <- mpg %>%
  group_by(class) %>% # group by grouping variable class
  summarize(hwy_sd = sd(hwy), hwy_mean = mean(hwy)) %>% # find the sd and se of the mean
  mutate(hwy_se = hwy_sd/sqrt(length(mpg$hwy))) # add the standard error of the mean

#build base bar plot  
p = ggplot(mpg, aes(x=class, y=hwy)) + geom_bar(stat="summary", fun.y="mean") + geom_jitter(aes(color=factor(year))) 

#add error bars
p + geom_errorbar(data=mpg_summary, aes(x=class, y=hwy_sd, ymin=hwy_mean-hwy_sd, ymax = hwy_mean+ hwy_sd))

Arranging Plots

Side by Side ggplot2 plots

require(gridExtra)
plot1 <- qplot(mtcars$mpg)
plot2 <- qplot(mtcars$disp)
grid.arrange(plot1, plot2, ncol=2)

Genomic Analysis

Volcano Plot

Volcano Plots with DESeq2

simple <- ggplot(df, aes(x = lfc, y = pvalue)) + 
  geom_point(size = 3, alpha = 0.7, na.rm = T) + # Make dots bigger
  theme_bw(base_size = 16) + # change theme
  ggtitle(label = "Volcano Plot", subtitle = "Simple black & white") + # Add a title
  xlab(expression(log[2]("Treatment" / "Untreated"))) + # x-axis label
  ylab(expression(-log[10]("adjusted p-value"))) + # y-axis label
  geom_vline(xintercept = c(-2,2), colour = "darkgrey") + # Add cutoffs
  geom_hline(yintercept = 1.3, colour = "darkgrey") + # Add cutoffs
  geom_vline(xintercept = 0, colour = "black") + # Add 0 lines
  scale_colour_gradient(low = "black", high = "black", guide = FALSE) + # Color black
  scale_x_continuous(limits = c(-4, 4)) # min/max of lfc

# Plot figure
simple

Color can be added like this.

# Modify dataset to add new coloumn of colors

data <- data %>%
  mutate(color = ifelse(data$lfc > 0 & data$pvalue > 1.3, 
                        yes = "Treated", 
                        no = ifelse(data$lfc < 0 & data$pvalue > 1.3, 
                                    yes = "Untreated", 
                                    no = "none")))

# Color corresponds to fold change directionality
colored <- ggplot(data, aes(x = lfc, y = pvalue)) + 
  geom_point(aes(color = factor(color)), size = 1.75, alpha = 0.8, na.rm = T) + # add gene points
  theme_bw(base_size = 16) + # clean up theme
  theme(legend.position = "none") + # remove legend 
  ggtitle(label = "Volcano Plot", subtitle = "Colored by directionality") +  # add title
  xlab(expression(log[2]("Treated" / "Untreated"))) + # x-axis label
  ylab(expression(-log[10]("adjusted p-value"))) + # y-axis label
  geom_vline(xintercept = 0, colour = "black") + # add line at 0
  geom_hline(yintercept = 1.3, colour = "black") + # p(0.05) = 1.3
  annotate(geom = "text", 
           label = "Untreated", 
           x = -2, y = 85, 
           size = 7, colour = "black") + # add Untreated text
  annotate(geom = "text", 
           label = "Treated", 
           x = 2, y = 85, 
           size = 7, colour = "black") + # add Treated text
  scale_color_manual(values = c("Treated" = "#E64B35", 
                                "Untreated" = "#3182bd", 
                                "none" = "#636363")) # change colors
  
# Plot figure
colored

Programming

For Loops

Assigning a new name to a variable each iteration with a for loop

Goal: With each iteration of the For() loop, create a new variable that appends the iterator variable to the new variable. This is useful for creating objects as you go through a list or sequence.

for (i in seq(1,6)){
 name <- paste("data", "_", i, sep = "")
 assign(name, read_excel("Left_Female.xlsx", i))
}

Miscellaneous

Remove objects by search term

rm(list=ls(pattern="temp"))

Make a list of data frames based on naming pattern

df_list = mget(ls(pattern = "df[0-9]"))
# this would match any object with "df" followed by a digit in its name
# you can test what objects will be got by just running the
ls(pattern = "df[0-9]")
# part and adjusting the pattern until it gets the right objects.

Note: The critical piece here is mget() which calls the value of a named object

Filter a list by class of list item

Filter(function(x) is(x, "data.frame"), mget(ls(pattern="_list"))) 

This is cool. ## Sequence Generation

Binary (0,1) Random Collection

Make binary vector with 0,1

X <- 10 
sample(c(0,1), replace=TRUE, size=X)

Annotate geom_point or other plot selectively using annotate()

ggplot XY scatter - how to change alpha transparency for select points?

## you start here
library(ggplot2)
special.points <- sample(1:n, 7)
## then add annotate text
ggplot(df, aes(x=SeqIdentityMean,
               y=SeqIdentityStdDev)) +
  geom_point(alpha=0.05) +
  annotate("point",
           df$SeqIdentityMean[special.points],
           df$SeqIdentityStdDev[special.points],
           col="red") +
  annotate("text",
           df$SeqIdentityMean[special.points],
           df$SeqIdentityStdDev[special.points],
           #text we want to display
           label=round(df$SeqIdentityStdDev[special.points],1),
           #adjust horizontal position of text
           hjust=-0.1)

Labels in ggplot2

Adding greek symbols Special characters in labels

#for use with ggplot2
labs(x = expression("Cross-sectional Area"*" ("*mu*"m"^{2}* ")"))   # microns. 
LS0tCnRpdGxlOiAiTXkgUiBDb2RlIE5vdGVzIgpvdXRwdXQ6CiAgaHRtbF9ub3RlYm9vazoKICAgIHRvYzogeWVzCiAgICB0b2NfZmxvYXQ6IHllcwogIGh0bWxfZG9jdW1lbnQ6CiAgICBkZl9wcmludDogcGFnZWQKICAgIHRvYzogeWVzCiAgICB0b2NfZmxvYXQ6IHllcwotLS0KCnRhZ3M9ICNyX2NvZGUgI2dncGxvdDIgI2RhdGFfc2NpZW5jZSAKCmBgYHtyIGdsb2JhbF9vcHRpb25zLCBpbmNsdWRlPUZBTFNFfQprbml0cjo6b3B0c19jaHVuayRzZXQoZmlnLndpZHRoPTEyLCBmaWcuaGVpZ2h0PTgsZXZhbD1UUlVFLCB3YXJuaW5nPUZBTFNFLCBtZXNzYWdlPUZBTFNFLCBlY2hvPVRSVUUpCmBgYAoKYGBge3J9CmxpYnJhcnkodGlkeXZlcnNlKQpgYGAKCiMgR2V0dGluZyBhbmQgV3JpdGluZyBEYXRhCgojIyBJbXBvcnRpbmcgRGF0YQoKIyMjIFRhYiBEZWxpbWl0ZWQgVGV4dApgYGAge3IgaW1wb3J0IHRhYmxlIGRlbGltaXRlZCB0ZXh0LCBldmFsPUZBTFNFLCBlY2hvPVRSVUV9Cm15ZGF0YSA8LSByZWFkLmRlbGltKCJteV9kYXRhLnR4dCIsIGhlYWRlcj1UUlVFLCBuYS5zdHJpbmdzPSJOQSIpCmBgYAoKIyMjIEltcG9ydGluZyBNdWx0aXBsZSBDU1YgZmlsZXMgYXQgb25jZQoKW1N0YWNrIE92ZXJmbG93IC0gSW1wb3J0aW5nIE11bHRpcGxlIENTViBmaWxlcyBpbiBSXShodHRwOi8vc3RhY2tvdmVyZmxvdy5jb20vcXVlc3Rpb25zLzExNDMzNDMyL2ltcG9ydGluZy1tdWx0aXBsZS1jc3YtZmlsZXMtaW50by1yKQoKYGBge3IgZXZhbD1GQUxTRSwgZWNobz1UUlVFfQp0ZW1wID0gbGlzdC5maWxlcyhwYXR0ZXJuPSIqLmNzdiIpCmZvciAoaSBpbiAxOmxlbmd0aCh0ZW1wKSkgYXNzaWduKHRlbXBbaV0sIHJlYWQuY3N2KHRlbXBbaV0pKSAgIApgYGAKCiMjIFdyaXRpbmcgT3V0CgojIyMgV3JpdGUgb3V0IGEgdGFiIGRlbGltaXRlZCB0ZXh0CgkKYGBge3IsIGV2YWw9RkFMU0UsIGVjaG89VFJVRX0KI3RhYiBkZWxpbWl0dGVkIHRleHQgZmlsZQp3cml0ZS50YWJsZShteV9kYXRhLCAibXlfZGF0YV9vdXRwdXQiLCBzZXA9Ilx0IikKYGBgCgoKCiMgV3JhbmdsaW5nCgoqKioKCiMjIERhdGEgRnJhbWVzCgojIyMgUmVtb3ZlIENvbHVtbnMKCltSZW1vdmUgYW4gZW50aXJlIGNvbHVtbiBmcm9tIGEgZGF0YS5mcmFtZSBpbiBSCl0oaHR0cHM6Ly9zdGFja292ZXJmbG93LmNvbS9xdWVzdGlvbnMvNjI4NjMxMy9yZW1vdmUtYW4tZW50aXJlLWNvbHVtbi1mcm9tLWEtZGF0YS1mcmFtZS1pbi1yKQoKUmVtb3ZlIGEgY29sdW1uCmBgYHtyIGV2YWw9RkFMU0UsIGVjaG89VFJVRX0KPiBEYXRhJGdlbm9tZSA8LSBOVUxMCj4gaGVhZChEYXRhKQogICBjaHIgcmVnaW9uCjEgY2hyMSAgICBDRFMKMiBjaHIxICAgZXhvbgozIGNocjEgICAgQ0RTCjQgY2hyMSAgIGV4b24KNSBjaHIxICAgIENEUwo2IGNocjEgICBleG9uCmBgYAoKUmVtb3ZlIG11bHRpcGxlIGNvbHVtbnMKYGBge3IsIGV2YWw9RkFMU0UsIGVjaG89VFJVRX0KRGF0YVsxOjJdIDwtIGxpc3QoTlVMTCkgICMgTWFyZWsKRGF0YVsxOjJdIDwtIE5VTEwgICAgICAgICMgZG9lcyBub3Qgd29yayEKYGBgCgojIyMgQWRkIGNvbHVtbnMgdG8gZGF0YSBmcmFtZSAKCltBZGRpbmcgYSBOZXcgQ29sdW1uIHRvIGEgRGF0YSBGcmFtZV0oaHR0cHM6Ly9sZW1icmEud29yZHByZXNzLmNvbS8yMDEwLzAzLzEyL2FkZGluZy1uZXctY29sdW1uLXRvLWEtZGF0YS1mcmFtZS1pbi1yLykKYGBge3IsIENyZWF0ZSBFbXB0eSBDb2x1bW4gaW4gZGYsIGV2YWw9RkFMU0UsIGVjaG89VFJVRX0KCmRmWyJNWV9ORVdfQ09MVU1OIl0gPC0gTkEgIyBUaGF0IGNyZWF0ZXMgdGhlIG5ldyBjb2x1bW4gbmFtZWQgIk1ZX05FV19DT0xVTU4iIGZpbGxlZCB3aXRoICJOQSIKZGZbYygiRSIsIkYiLCJHIiwiSCIsIkkiKV0gPC0gTkEgI2NyZWF0ZSBtdWx0aXBsZSBlbXB0eSBjb2x1bW5zCmBgYAoKU2VlIGFsc28gW0FkZGluZyBhbmQgUmVtb3ZpbmcgQ29sdW1ucyAtIFIgQ29va2Jvb2tdKGh0dHA6Ly93d3cuY29va2Jvb2stci5jb20vTWFuaXB1bGF0aW5nX2RhdGEvQWRkaW5nX2FuZF9yZW1vdmluZ19jb2x1bW5zX2Zyb21fYV9kYXRhX2ZyYW1lLykKCgojIyMgQWRkIEdyb3VwIEluZm9ybWF0aW9uIHRvIFN1YmplY3RzCgpXaGVuIHlvdSB3YW50IHRvIHB1dCBhbiBvYnNlcnZhdGlvbiBpbnRvIGEgZ3JvdXAgKGUuZyBDYXNlIHZzLiBDb250cm9sKSwgeW91IG5lZWQgdG8gY3JlYXRlIGEgbmV3IHZhcmlhYmxlLiBBIHNpbXBsZSB3YXkgdG8gZG8gdGhpcyBpcyB0byBjcmVhdGUgYSB2ZWN0b3IocykgdGhhdCBjb250YWlucyB0aGUgR3JvdXAgbWVtYmVycyBhbmQgdGhlbiB1c2UgYG11dGF0ZWAgd2l0aCBgaWZfZWxzZSgpYC4KCkluIHRoaXMgZXhhbXBsZSwgSSBjcmVhdGVkIGEgZ3JvdXAgY2FsbGVkICJEb21lc3RpYyIgZm9yIHNvbWUgY2FycyBiYXNlZCBvbiB0aGVpciBtYW51ZmFjdHVyZXIgdmFyaWFibGUuCgpgYGB7ciBldmFsPVRSVUUsIGVjaG89VFJVRX0KZG9tZXN0aWMgPSBjKCJjaGV2cm9sZXQiLCAiZG9kZ2UiLCAiZm9yZCIsICJqZWVwIiwgImxpbmNvbG4iLCAibWVyY3VyeSIsICJwb250aWFjIikKbXV0YXRlKG1wZywgTG9jYXRpb24gPSBpZl9lbHNlKG1hbnVmYWN0dXJlciAlaW4lIGRvbWVzdGljLCAiRG9tZXN0aWMiLCAiRm9yZWlnbiIpKQpgYGAKCiMjIyBNZXJnZSBEYXRhIEZyYW1lcyBieSBWYXJpYWJsZQoKYGBge3IsIGVjaG89VFJVRSwgZXZhbD1GQUxTRX0KZGF0YSA8LSBtZXJnZShkMSwgZDIsIGJ5PSJQYXRpZW50LklEIikKYGBgCgojIyMgQ3JlYXRlIG11bHRpcGxlIGNvbHVtbnMgd2l0aCBkaWZmZXJlbnQgbmFtZXMgYW5kIGRhdGEKW0FkZGluZyBtdWx0aXBsZSBjb2x1bW5zIHNpbXVsdGFuZW91c2x5IHdpdGggZGlmZmVyZW50IHZhbHVlcyBpbiBSCi1TT10oaHR0cHM6Ly9zdGFja292ZXJmbG93LmNvbS9xdWVzdGlvbnMvNDgzNTE3NTAvYWRkaW5nLW11bHRpcGxlLWNvbHVtbnMtc2ltdWx0YW5lb3VzbHktd2l0aC1kaWZmZXJlbnQtdmFsdWVzLWluLXIvNDgzNTE4MTAjNDgzNTE4MTApCmBgYHtyLCBjcmVhdGUgY29sdW1ucyB3aXRoIGRpZmZlcmVudCBuYW1lcyBhbmQgdmFsdWVzfQpsaWJyYXJ5KCd0aWR5dmVyc2UnKQoKZGYgPC0gaGVhZChjYXJzKQoKbXV0YXRlKGRmLCBTdWJqZWN0ID0gJ0YxJywgU2xpZGUgPSAnMUEnKQoKYGBgCgojIyMgTWVyZ2UgbXVsdGlwbGUgZGF0YSBmcmFtZXMgdXNpbmcgYG1lcmdlX2FsbCgpCgpgYGB7ciwgZXZhbD1GQUxTRSwgZWNobz1UUlVFfQpyZXNoYXBlOjptZXJnZV9hbGwoeW91cl9saXN0X3dpdGhfZGZzLCAuLi4pCmBgYAoKKioqCgojIyBGYWN0b3JzIGFuZCBDYXRlZ29yaWVzCgojIyMgQ2hhbmdlIE9yZGVyIG9mIEZhY3RvciBMZXZlbHMgCgpgYGB7ciwgZWNobz1UUlVFLCBldmFsPUZBTFNFfQpwYyRHcm91cCA8LSBmYWN0b3IocGMkR3JvdXAsIGxldmVscyA9IGMoJ0NvbnRyb2wnLCdDYXNlJyksb3JkZXJlZCA9IFRSVUUpCmBgYAoKCiMjIyBDYXRlZ29yaXppbmcgQ29udGludW91cyBEYXRhIAoKUHVycG9zZTogV2hlbiB5b3Ugd2FudCB0YWtlIG51bWVyaWNhbCB2YWx1ZXMgYW5kIGJpbiB0aGVtIGludG8gY2F0ZWdvcmllcy4KClVzZSBjYXNlczogIAoKLSBJIGhhdmUgZXhwcmVzc2lvbiB2YWx1ZXMgb2YgSVNIIHB1bmN0YSBpbnNpZGUgY2VsbHMuIEkgd2FudCB0byBjYXRlZ29yaXplIGV4cHJlc3Npb24gbGV2ZWxzIGludG8gZGlzY3JldGUgY2F0ZWdvcmllcyAoaGlnaCwgbWVkaXVtLCBsb3cpLgoKW0NhdGVnb3JpemUgY29udGludW91cyBkYXRhXShodHRwczovL3N0YWNrb3ZlcmZsb3cuY29tL3F1ZXN0aW9ucy80MDM4MDExMi9jYXRlZ29yaXplLWNvbnRpbnVvdXMtdmFyaWFibGUtd2l0aC1kcGx5cikKCldpdGggW2BjdXRgXShodHRwczovL3d3dy5yZG9jdW1lbnRhdGlvbi5vcmcvcGFja2FnZXMvYmFzZS92ZXJzaW9ucy8zLjQuMy90b3BpY3MvY3V0KQpgYGB7ciwgZWNobz1UUlVFLCBldmFsPUZBTFNFfQpkZiRjYXRlZ29yeSA8LSBjdXQoZGYkYSwgYnJlYWtzPWMoLUluZiwgMC41LCAwLjYsIEluZiksIGxhYmVscz1jKCJsb3ciLCJtaWRkbGUiLCJoaWdoIikpCmBgYAoKV2l0aCBbYGRwbHlyYF0oaHR0cDovL2RwbHlyLnRpZHl2ZXJzZS5vcmcvcmVmZXJlbmNlL211dGF0ZS5odG1sKQpgYGB7ciwgZXZhbD1GQUxTRSwgZWNobz1UUlVFfQpyZXMgPC0gZGYgJT4lIG11dGF0ZShjYXRlZ29yeT1jdXQoYSwgYnJlYWtzPWMoLUluZiwgMC41LCAwLjYsIEluZiksIGxhYmVscz1jKCJsb3ciLCJtaWRkbGUiLCJoaWdoIikpKQpgYGAKCioqKgoKIyMgU3Vic2V0dGluZwoKIyMjIGBzZWxlY3QoKWAgZnJvbSBgZHBseXIoKWAKCj7igJxUaGUgc2VsZWN0KCkgZnVuY3Rpb24gY2FuIGJlIHVzZWQgdG8gc2VsZWN0IGNvbHVtbnMgb2YgYSBkYXRhIGZyYW1lIHRoYXQgeW91IHdhbnQgdG8gZm9jdXMgb24uIE9mdGVuIHlvdeKAmWxsIGhhdmUgYSBsYXJnZSBkYXRhIGZyYW1lIGNvbnRhaW5pbmcg4oCcYWxs4oCdIG9mIHRoZSBkYXRhLCBidXQgYW55IGdpdmVuIGFuYWx5c2lzIG1pZ2h0IG9ubHkgdXNlIGEgc3Vic2V0IG9mIHZhcmlhYmxlcyBvciBvYnNlcnZhdGlvbnMuIFRoZSBzZWxlY3QoKSBmdW5jdGlvbiBhbGxvd3MgeW91IHRvIGdldCB0aGUgZmV3IGNvbHVtbnMgeW91IG1pZ2h0IG5lZWQu4oCdIC0gRXhjZXJwdCBGcm9tOiBSb2dlciBELiBQZW5nLiDigJxSIFByb2dyYW1taW5nIGZvciBEYXRhIFNjaWVuY2Uu4oCdIGlCb29rcy4gCgotIFtgc2VsZWN0KClgIC0gUjREUyBdKGh0dHA6Ly9yNGRzLmhhZC5jby5uei90cmFuc2Zvcm0uaHRtbCNzZWxlY3QtY29sdW1ucy13aXRoLXNlbGVjdCkKLSBbYHNlbGVjdCgpYCAtIFRpZHl2ZXJzZSBdKGh0dHA6Ly9kcGx5ci50aWR5dmVyc2Uub3JnL3JlZmVyZW5jZS9zZWxlY3QuaHRtbCkKCmBgYHtyIHNlbGVjdCwgZXZhbD1GQUxTRSwgZWNobz1UUlVFfQo+IG5hbWVzKGNoaWNhZ28pWzE6M10KWzFdICJjaXR5IiAidG1wZCIgImRwdHAiCj4gc3Vic2V0IDwtIHNlbGVjdChjaGljYWdvLCBjaXR5OmRwdHApCj4gaGVhZChzdWJzZXQpCiAgY2l0eSB0bXBkICAgZHB0cAoxIGNoaWMgMzEuNSAzMS41MDAKMiBjaGljIDMzLjAgMjkuODc1CjMgY2hpYyAzMy4wIDI3LjM3NQo0IGNoaWMgMjkuMCAyOC42MjUKNSBjaGljIDMyLjAgMjguODc1CjYgY2hpYyA0MC4wIDM1LjEyNQoKYGBgCgojIEZ1bmN0aW9ucwoKIyMgQXNzaWduIHRoZSBvdXRwdXQgb2YgYSBmdW5jdGlvbiB0byB0aGUgZ2xvYmFsIGVudmlyb25tZW50CllvdSBnZXQgc29tZSBvdXRwdXQgZnJvbSB0aGUgZnVuY3Rpb24gYW5kIHlvdSB3YW50IGl0IHRvIGJlIHN0b3JlZCBpbiB0aGUgZ2xvYmFsIGVudmlyb25tZW50IHNvIGl0IGNhbiBiZSB1c2VkIGVsc2V3aGVyZS4gCgpGcm9tIFtTT10oaHR0cHM6Ly9zdGFja292ZXJmbG93LmNvbS9xdWVzdGlvbnMvMzc1NTg0MzEvc3RvcmUtdmFyaWFibGVzLWZyb20tYS1mdW5jdGlvbi1pbi10aGUtZ2xvYmFsLWVudmlyb25tZW50KToKCmBhc3NpZ24oKWAgb3IgYDw8LWAgY2FuIGJvdGggYmUgdXNlZC4gCgo+IFlvdSBjb3VsZCB1c2UgYXNzaWduOiAgIAoKPiBgYXNzaWduKCJ2IiwiaGkiLGVudmlyID0gZ2xvYmFsZW52KCkpYCAgIAoKPlRoaXMgcmVxdWlyZXMgdGhhdCB5b3UgaGF2ZSB0aGUgbmFtZSBvZiB0aGUgdGFyZ2V0IGdsb2JhbCB2YXJpYWJsZSBhcyBhIHN0cmluZywgYnV0IGl0IGNhbiBiZSBlYXN5IHRvIGRvIHRoaXMgZXZlbiB3aXRoIGEgdmVjdG9yIG9mIGRvemVucyBvZiBzdWNoIHRoaW5ncy4KCj5UaGlzIHF1ZXN0aW9uIGRpc2N1c3NlcyB0aGUgZGlmZmVyZW5jZXMgYmV0d2VlbiBhc3NpZ24gYW5kIDw8LS4gVGhlIGNoaWVmIGRpZmZlcmVuY2UgaXMgdGhhdCBhc3NpZ24gbGV0cyB5b3Ugc3BlY2lmeSB0aGUgZW52aXJvbm1lbnQgLS0gc28gaXQgaXMgZWFzeSB0byB1c2UgaXQgdG8gc3RvcmUgZGF0YSBpbiBhIG5vbi1nbG9iYWwgYnV0IHBlcnNpc3RlbnQgZW52aXJvbm1lbnQgc28gdGhhdCB5b3UgY291bGQgZS5nLiBlbXVsYXRlIHN0YXRpYyB2YXJpYWJsZXMgaW4gUi4gV2hpbGUgaXQgaXMgcG9zc2libGUgdG8gdXNlIGFzc2lnbiB0byBtb2RpZnkgdGhlIGdsb2JhbCBlbnZpcm9ubWVudCwgeW91IHNob3VsZCBiZSBhd2FyZSB0aGF0IGl0IGlzIHNlbGRvbSBhIGdvb2QgdGhpbmcgdG8gZG8gc28uIFRoZXJlIGlzIHRvbyBtdWNoIG9mIGEgZGFuZ2VyIG9mIGFjY2lkZW50YWxseSBvdmVyd3JpdGluZyBkYXRhIHRoYXQgeW91IGRvbid0IHdhbnQgdG8gaGF2ZSBvdmVyd3JpdHRlbi4gQ29kZSB3aGljaCBtYWtlcyBoZWF2eSB1c2Ugb2YgZ2xvYmFsIHZhcmlhYmxlcyBjYW4gYWxtb3N0IGFsd2F5cyBiZSByZWZhY3RvcmVkIGludG8gY2xlYW5lciBjb2RlIHdoaWNoIGRvZXNuJ3QuIElmIHlvdSBuZWVkIHRvIGdldCBhIGxvdCBvZiBoZXRlcm9nZW5lb3VzIGRhdGEgZnJvbSBhIGZ1bmN0aW9uIHRvIHRoZSBjYWxsaW5nIGVudmlyb25tZW50LCB0aGUgY2xlYW5lc3Qgc29sdXRpb24gd291bGQgYmUgdG8gcmV0dXJuIHRoZSBuZWVkZWQgZGF0YSBpbiBhIGxpc3QuCgoKCiMgRGVzY3JpYmluZyBEYXRhCgojIyBTdW1tYXJ5IFN0YXRpc3RpY3MKCiMjIyBgc3VtbWFyaXplKClgIGZyb20gYGRwbHlyYAoKYGBge3Igc3VtbWFyaXplIHdpdGggZHBseXIsIGV2YWw9VFJVRSwgZWNobz1UUlVFfQoKbXBnX3N1bW1hcnkgPC0gbXBnICU+JQogIGdyb3VwX2J5KGNsYXNzKSAlPiUgIyBncm91cCBieSBncm91cGluZyB2YXJpYWJsZSBjbGFzcwogIHN1bW1hcml6ZShod3lfc2QgPSBzZChod3kpLCBod3lfbWVhbiA9IG1lYW4oaHd5KSkgJT4lICMgZmluZCB0aGUgc2QgYW5kIHNlIG9mIHRoZSBtZWFuCiAgbXV0YXRlKGh3eV9zZSA9IGh3eV9zZC9zcXJ0KGxlbmd0aChtcGckaHd5KSkpICMgYWRkIHRoZSBzdGFuZGFyZCBlcnJvciBvZiB0aGUgbWVhbgoKbXBnX3N1bW1hcnkKYGBgCgoKIyMjIGBzdW1tYXJ5QnlgIChCYXNlIFIpCmBgYHtyIHN1bW1hcnlCeSwgZXZhbD1GQUxTRSwgZWNobz1UUlVFfQpzdW1tYXJ5QnkoQWdlIH4gbWVkX3llYXIsIGRhdGEgPSBkYXRhLCBGVU4gPSBmdW5jdGlvbih4KSB7IGMobSA9IG1lYW4oeCwgbmEucm09VFJVRSksIHMgPSBzZCh4LCBuYS5ybT1UUlVFKSwgbiA9bGVuZ3RoKHgpLCByPXJhbmdlKHgsbmEucm09VFJVRSkpIH0KICAgICAgICAgIApgYGAKCQojIyMgU3VtbWFyaXplIHdpdGggYHBzeWNoKCApYCBwYWNrYWdlCgpgYGB7ciBzdW1tYXJ5UHN5Y2gsIGV2YWw9RkFMU0UsIGVjaG89VFJVRX0KbGlicmFyeShwc3ljaCkKZGVzY3JpYmVCeShteV9kYXRhLCBteV9kYXRhJEdyb3VwLCBtYXQ9VCkKYGBgCgojIyMgQ291bnRpbmcgbnVtYmVyIG9mIHZhbHVlcyBpbiBhIGRhdGFmcmFtZSBjb2x1bW4KCkJhc2UgUgpgYGB7ciwgZXZhbD1GLCBlY2hvPVR9Cmxlbmd0aCh3aGljaChkYXRhX3Jlc2hhcGVkJFBvcHVsYXRpb249PSJWZ2x1dDEtQ2hSMiIpKQpgYGAKCltgVGlkeXZlcnNlYCB3YXkgdXNpbmcgYGNvdW50KClgIG9yIGB0YWxseSgpYF0oaHR0cDovL2RwbHlyLnRpZHl2ZXJzZS5vcmcvcmVmZXJlbmNlL3RhbGx5Lmh0bWwpCgpgYGB7ciBldmFsPUYsIGVjaG89VH0KdGFsbHkoeCwgd3QsIHNvcnQgPSBGQUxTRSkKCmNvdW50KHgsIC4uLiwgd3QgPSBOVUxMLCBzb3J0ID0gRkFMU0UpCgphZGRfdGFsbHkoeCwgd3QsIHNvcnQgPSBGQUxTRSkKCmFkZF9jb3VudCh4LCAuLi4sIHd0ID0gTlVMTCwgc29ydCA9IEZBTFNFKQpgYGAKCgoKKioqCgoKCgojIFZpc3VhbGl6ZQoKIyMgZ2dwbG90MgoKZ2dwbG90MiBpcyBhIHBhY2thZ2UgdGhhdCBpcyB1c2VkIHRvIG1ha2UgYXR0cmFjdGl2ZSBhbmQgZmxleGlibGUgcGxvdHMuIEl0J3MgYmFzZWQgb24gdGhlIGlkZWEgb2YgZ3JhcGhpY3MgY29tcG9zZWQgb2YgdmFyaW91cyBsYXllcnMuCgo+IE91ciBuZXcgdGVtcGxhdGUgdGFrZXMgc2V2ZW4gcGFyYW1ldGVycywgdGhlIGJyYWNrZXRlZCB3b3JkcyB0aGF0IGFwcGVhciBpbiB0aGUgdGVtcGxhdGUuIEluIHByYWN0aWNlLCB5b3UgcmFyZWx5IG5lZWQgdG8gc3VwcGx5IGFsbCBzZXZlbiBwYXJhbWV0ZXJzIHRvIG1ha2UgYSBncmFwaCBiZWNhdXNlIGdncGxvdDIgd2lsbCBwcm92aWRlIHVzZWZ1bCBkZWZhdWx0cyBmb3IgZXZlcnl0aGluZyBleGNlcHQgdGhlIGRhdGEsIHRoZSBtYXBwaW5ncywgYW5kIHRoZSBnZW9tIGZ1bmN0aW9uLgoKPiBUaGUgc2V2ZW4gcGFyYW1ldGVycyBpbiB0aGUgdGVtcGxhdGUgY29tcG9zZSB0aGUgZ3JhbW1hciBvZiBncmFwaGljcywgYSBmb3JtYWwgc3lzdGVtIGZvciBidWlsZGluZyBwbG90cy4gVGhlIGdyYW1tYXIgb2YgZ3JhcGhpY3MgaXMgYmFzZWQgb24gdGhlIGluc2lnaHQgdGhhdCB5b3UgY2FuIHVuaXF1ZWx5IGRlc2NyaWJlIGFueSBwbG90IGFzIGEgY29tYmluYXRpb24gb2YgYSBkYXRhc2V0LCBhIGdlb20sIGEgc2V0IG9mIG1hcHBpbmdzLCBhIHN0YXQsIGEgcG9zaXRpb24gYWRqdXN0bWVudCwgYSBjb29yZGluYXRlIHN5c3RlbSwgYW5kIGEgZmFjZXRpbmcgc2NoZW1lLiAoaHR0cDovL3I0ZHMuaGFkLmNvLm56L2RhdGEtdmlzdWFsaXNhdGlvbi5odG1sI2ZpcnN0LXN0ZXBzKQoKYGBgCmdncGxvdChkYXRhID0gPERBVEE+KSArIAogIDxHRU9NX0ZVTkNUSU9OPigKICAgICBtYXBwaW5nID0gYWVzKDxNQVBQSU5HUz4pLAogICAgIHN0YXQgPSA8U1RBVD4sIAogICAgIHBvc2l0aW9uID0gPFBPU0lUSU9OPgogICkgKwogIDxDT09SRElOQVRFX0ZVTkNUSU9OPiArCiAgPEZBQ0VUX0ZVTkNUSU9OPgpgYGAKCkFuZCBmcm9tIFtzb2N2aXpdKGh0dHA6Ly9zb2N2aXouY28vbWFrZXBsb3QuaHRtbCNidWlsZC15b3VyLXBsb3RzLWxheWVyLWJ5LWxheWVyKQoKPgotIFRlbGwgdGhlIGdncGxvdCgpIGZ1bmN0aW9uIHdoYXQgb3VyIGRhdGEgaXMuVGhlIGRhdGEgPSDigKYgc3RlcC4KLSBUZWxsIGdncGxvdCgpIHdoYXQgcmVsYXRpb25zaGlwcyB3ZSB3YW50IHRvIHNlZS5UaGUgIG1hcHBpbmcgPSBhZXMo4oCmKSBzdGVwLiBGb3IgY29udmVuaWVuY2Ugd2Ugd2lsbCBwdXQgdGhlIHJlc3VsdHMgb2YgdGhlIGZpcnN0IHR3byBzdGVwcyBpbiBhbiBvYmplY3QgY2FsbGVkIHAuCi0gVGVsbCBnZ3Bsb3QgaG93IHdlIHdhbnQgdG8gc2VlIHRoZSByZWxhdGlvbnNoaXBzIGluIG91ciBkYXRhLkNob29zZSBhIGdlb20uCi0gTGF5ZXIgb24gZ2VvbXMgYXMgbmVlZGVkLCBieSBhZGRpbmcgdGhlbSB0byB0aGUgcCBvYmplY3Qgb25lIGF0IGEgdGltZS4KLSBVc2UgVGhlIHNjYWxlXywgZmFtaWx5LCBsYWJzKCkgYW5kIGd1aWRlcygpIGZ1bmN0aW9ucy4gc29tZSBhZGRpdGlvbmFsIGZ1bmN0aW9ucyB0byBhZGp1c3Qgc2NhbGVzLCBsYWJlbHMsIHRpY2sgbWFya3MsIHRpdGxlcy4gV2XigJlsbCBsZWFybiBtb3JlIGFib3V0IHNvbWUgb2YgdGhlc2UgZnVuY3Rpb25zIHNob3J0bHkuCgoqKioKCiMjIEhpc3RvZ3JhbXMKCiMjIyBCYXNpYyBIaXN0b2dyYW0gKGdncGxvdDIpCgotIFRha2UgdGhlIGRhdGEgYW5kIGFkZCBhIGBnZW9tX2hpc3RvZ3JhbWAgbGF5ZXIgd2l0aCBgYWVzKClgIHNldCB0byB0aGUgdmFyaWFibGUgeW91J3JlIGludGVyZXN0ZWQgaW4uCgpgYGB7ciwgYmFzaWMgZ2dwbG90MiBoaXN0b2dyYW0sIGV2YWw9VFJVRSwgZWNobz1UUlVFfQpnZ3Bsb3QobXRjYXJzKSArIGdlb21faGlzdG9ncmFtKGFlcyhtcGcpLCBiaW53aWR0aCA9IDUpCmBgYAoKCiMjIyBUd28gaGlzdG9ncmFtcyBvdmVybGFpZCAoZ2dwbG90MikKCi0gVGFrZSBhIGRhdGFzZXQuIE1ha2UgYSBoaXN0b2dyYW0sIHVzZSBgYWVzKClgIGZvciB0aGUgdmFyaWFibGUgb2YgaW50ZXJlc3QuIEFkZCBhbm90aGVyIGBnZW9tX2hpc3QoKWAgbGF5ZXIgZG9pbmcgdGhlIHNhbWUuIFlvdSBjYW4gY2hhbmdlIGBiaW53aWR0aGAgYW5kIGBhbHBoYWAKCgpgYGB7ciwgT3ZlcmxhaWQgSGlzdG9ncmFtcywgZXZhbD1UUlVFLCBlY2hvPVRSVUV9CmdncGxvdChtdGNhcnMpICsgZ2VvbV9oaXN0b2dyYW0oYWVzKGRpc3ApLCBmaWxsPSJyZWQiLCBhbHBoYT0wLjIsIGJpbndpZHRoID0gMTAwKSAgKyBnZW9tX2hpc3RvZ3JhbShhZXMoaHApLCBmaWxsPSJibHVlIiwgYWxwaGE9MC4yLCBiaW53aWR0aCA9IDEwMCkKCmBgYAoKKioqCiMjIFNjYXR0ZXIgUGxvdHMKCmBgYHtyLCBldmFsPVRSVUUsIGVjaG89VFJVRX0KI3VzaW5nIGdncGxvdDIKCmdncGxvdChtdGNhcnMpICsgZ2VvbV9wb2ludChhZXMoeCA9IG1wZywgeSA9IGRpc3AsIGNvbG9yPSBtcGcgPjI1KSkKCgpgYGAKCiMjIE1vc2FpYyBQbG90CgpGb3IgQ2F0ZWdvcmljYWwgeCBDYXRlZ29yaWNhbCBEYXRhIChDb250aW5nZW5jeSBUYWJsZSkKCltgZ2dtb3NpYWNgIHBhY2thZ2VdKGh0dHBzOi8vY3Jhbi5yLXByb2plY3Qub3JnL3dlYi9wYWNrYWdlcy9nZ21vc2FpYy92aWduZXR0ZXMvZ2dtb3NhaWMuaHRtbCkKCmBgYHtyIE1vc2FpYyBQbG90IEZ1bmN0aW9ufQoKbGlicmFyeShnZ21vc2FpYykKbGlicmFyeShOSEFORVMpCiBnZ3Bsb3QoZGF0YSA9IE5IQU5FUykgKwogICBnZW9tX21vc2FpYyhhZXMod2VpZ2h0ID0gV2VpZ2h0LCB4ID0gcHJvZHVjdChTbGVlcEhyc05pZ2h0LCBBZ2VEZWNhZGUpLCBmaWxsPWZhY3RvcihTbGVlcEhyc05pZ2h0KSksIG5hLnJtPVRSVUUpICsgICAgdGhlbWUoYXhpcy50ZXh0Lng9ZWxlbWVudF90ZXh0KGFuZ2xlPS0yNSwgaGp1c3Q9IC4xKSkgKyBsYWJzKHg9IkFnZSBpbiBEZWNhZGVzICIsIHRpdGxlPSdmKFNsZWVwSHJzTmlnaHQgfCBBZ2VEZWNhZGUpIGYoQWdlRGVjYWRlKScpICsgZ3VpZGVzKGZpbGw9Z3VpZGVfbGVnZW5kKHRpdGxlID0gIlNsZWVwSHJzTmlnaHQiLCByZXZlcnNlID0gVFJVRSkpCmBgYAoKIyMgQmFyIFBsb3RzCgojIyMgQmFzaWMgQmFyIFBsb3Qgd2l0aCBPdmVybGFpZCBQb2ludHMKCi0gRWFjaCBnZW9tIGlzIGEgbGF5ZXIuIFRvIGdldCB0aGUgbWVhbiBvZiB0aGUgZGF0YV9wb2ludHMsIG5vIGNvbnZlcnNpb24gaXMgbmVjZXNzYXJ5LiBZb3UgY2FuIHVzZSB0aGUgYHN0YXQ9c3VtbWFyeWAgYXJndW1lbnQuIEVycm9yIGJhcnMsIGhvd2V2ZXIsIG11c3QgYmUgY2FsY3VsYXRlZCBhbmQgZW50ZXJlZCBpbnRvIHRoZSBkYXRhLiAKCltQbG90dGluZyBtZWFucyBhbmQgZXJyb3IgYmFycyAtIFIgQ29va2Jvb2tdKGh0dHA6Ly93d3cuY29va2Jvb2stci5jb20vR3JhcGhzL1Bsb3R0aW5nX21lYW5zX2FuZF9lcnJvcl9iYXJzXyhnZ3Bsb3QyKS8pCgoKYGBge3IgYmFyIHBsb3RzIG1lYW5zIGFuZCBlcnJvciBiYXJzLCBldmFsPVRSVUV9CgojZ2V0IHN1bW1hcnkgc3RhdGlzdGljcwptcGdfc3VtbWFyeSA8LSBtcGcgJT4lCiAgZ3JvdXBfYnkoY2xhc3MpICU+JSAjIGdyb3VwIGJ5IGdyb3VwaW5nIHZhcmlhYmxlIGNsYXNzCiAgc3VtbWFyaXplKGh3eV9zZCA9IHNkKGh3eSksIGh3eV9tZWFuID0gbWVhbihod3kpKSAlPiUgIyBmaW5kIHRoZSBzZCBhbmQgc2Ugb2YgdGhlIG1lYW4KICBtdXRhdGUoaHd5X3NlID0gaHd5X3NkL3NxcnQobGVuZ3RoKG1wZyRod3kpKSkgIyBhZGQgdGhlIHN0YW5kYXJkIGVycm9yIG9mIHRoZSBtZWFuCgojYnVpbGQgYmFzZSBiYXIgcGxvdCAgCnAgPSBnZ3Bsb3QobXBnLCBhZXMoeD1jbGFzcywgeT1od3kpKSArIGdlb21fYmFyKHN0YXQ9InN1bW1hcnkiLCBmdW4ueT0ibWVhbiIpICsgZ2VvbV9qaXR0ZXIoYWVzKGNvbG9yPWZhY3Rvcih5ZWFyKSkpIAoKI2FkZCBlcnJvciBiYXJzCnAgKyBnZW9tX2Vycm9yYmFyKGRhdGE9bXBnX3N1bW1hcnksIGFlcyh4PWNsYXNzLCB5PWh3eV9zZCwgeW1pbj1od3lfbWVhbi1od3lfc2QsIHltYXggPSBod3lfbWVhbisgaHd5X3NkKSkKCmBgYAoKCgoKIyMgQXJyYW5naW5nIFBsb3RzCgpbU2lkZSBieSBTaWRlIGdncGxvdDIgcGxvdHNdKGh0dHBzOi8vc3RhY2tvdmVyZmxvdy5jb20vcXVlc3Rpb25zLzEyNDk1NDgvc2lkZS1ieS1zaWRlLXBsb3RzLXdpdGgtZ2dwbG90MikKCmBgYHtyIHNpZGUgYnkgc2lkZSBnZ3Bsb3QyIHBsb3RzLCBldmFsPVRSVUUsIGVjaG89VFJVRX0KcmVxdWlyZShncmlkRXh0cmEpCnBsb3QxIDwtIHFwbG90KG10Y2FycyRtcGcpCnBsb3QyIDwtIHFwbG90KG10Y2FycyRkaXNwKQpncmlkLmFycmFuZ2UocGxvdDEsIHBsb3QyLCBuY29sPTIpCmBgYAoKCioqKgoKIyBHZW5vbWljIEFuYWx5c2lzCgojIyBWb2xjYW5vIFBsb3QKCltWb2xjYW5vIFBsb3RzIHdpdGggREVTZXEyXShodHRwczovL3R3YmF0dGFnbGlhLmdpdGh1Yi5pby8yMDE2LzEyLzE3L3ZvbGNhbm8tcGxvdC8pCgpgYGB7ciBldmFsPUZBTFNFfQpzaW1wbGUgPC0gZ2dwbG90KGRmLCBhZXMoeCA9IGxmYywgeSA9IHB2YWx1ZSkpICsgCiAgZ2VvbV9wb2ludChzaXplID0gMywgYWxwaGEgPSAwLjcsIG5hLnJtID0gVCkgKyAjIE1ha2UgZG90cyBiaWdnZXIKICB0aGVtZV9idyhiYXNlX3NpemUgPSAxNikgKyAjIGNoYW5nZSB0aGVtZQogIGdndGl0bGUobGFiZWwgPSAiVm9sY2FubyBQbG90Iiwgc3VidGl0bGUgPSAiU2ltcGxlIGJsYWNrICYgd2hpdGUiKSArICMgQWRkIGEgdGl0bGUKICB4bGFiKGV4cHJlc3Npb24obG9nWzJdKCJUcmVhdG1lbnQiIC8gIlVudHJlYXRlZCIpKSkgKyAjIHgtYXhpcyBsYWJlbAogIHlsYWIoZXhwcmVzc2lvbigtbG9nWzEwXSgiYWRqdXN0ZWQgcC12YWx1ZSIpKSkgKyAjIHktYXhpcyBsYWJlbAogIGdlb21fdmxpbmUoeGludGVyY2VwdCA9IGMoLTIsMiksIGNvbG91ciA9ICJkYXJrZ3JleSIpICsgIyBBZGQgY3V0b2ZmcwogIGdlb21faGxpbmUoeWludGVyY2VwdCA9IDEuMywgY29sb3VyID0gImRhcmtncmV5IikgKyAjIEFkZCBjdXRvZmZzCiAgZ2VvbV92bGluZSh4aW50ZXJjZXB0ID0gMCwgY29sb3VyID0gImJsYWNrIikgKyAjIEFkZCAwIGxpbmVzCiAgc2NhbGVfY29sb3VyX2dyYWRpZW50KGxvdyA9ICJibGFjayIsIGhpZ2ggPSAiYmxhY2siLCBndWlkZSA9IEZBTFNFKSArICMgQ29sb3IgYmxhY2sKICBzY2FsZV94X2NvbnRpbnVvdXMobGltaXRzID0gYygtNCwgNCkpICMgbWluL21heCBvZiBsZmMKCiMgUGxvdCBmaWd1cmUKc2ltcGxlCgpgYGAKCkNvbG9yIGNhbiBiZSBhZGRlZCBsaWtlIHRoaXMuCgpgYGB7ciBldmFsPUZBTFNFfQojIE1vZGlmeSBkYXRhc2V0IHRvIGFkZCBuZXcgY29sb3VtbiBvZiBjb2xvcnMKCmRhdGEgPC0gZGF0YSAlPiUKICBtdXRhdGUoY29sb3IgPSBpZmVsc2UoZGF0YSRsZmMgPiAwICYgZGF0YSRwdmFsdWUgPiAxLjMsIAogICAgICAgICAgICAgICAgICAgICAgICB5ZXMgPSAiVHJlYXRlZCIsIAogICAgICAgICAgICAgICAgICAgICAgICBubyA9IGlmZWxzZShkYXRhJGxmYyA8IDAgJiBkYXRhJHB2YWx1ZSA+IDEuMywgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHllcyA9ICJVbnRyZWF0ZWQiLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbm8gPSAibm9uZSIpKSkKCiMgQ29sb3IgY29ycmVzcG9uZHMgdG8gZm9sZCBjaGFuZ2UgZGlyZWN0aW9uYWxpdHkKY29sb3JlZCA8LSBnZ3Bsb3QoZGF0YSwgYWVzKHggPSBsZmMsIHkgPSBwdmFsdWUpKSArIAogIGdlb21fcG9pbnQoYWVzKGNvbG9yID0gZmFjdG9yKGNvbG9yKSksIHNpemUgPSAxLjc1LCBhbHBoYSA9IDAuOCwgbmEucm0gPSBUKSArICMgYWRkIGdlbmUgcG9pbnRzCiAgdGhlbWVfYncoYmFzZV9zaXplID0gMTYpICsgIyBjbGVhbiB1cCB0aGVtZQogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikgKyAjIHJlbW92ZSBsZWdlbmQgCiAgZ2d0aXRsZShsYWJlbCA9ICJWb2xjYW5vIFBsb3QiLCBzdWJ0aXRsZSA9ICJDb2xvcmVkIGJ5IGRpcmVjdGlvbmFsaXR5IikgKyAgIyBhZGQgdGl0bGUKICB4bGFiKGV4cHJlc3Npb24obG9nWzJdKCJUcmVhdGVkIiAvICJVbnRyZWF0ZWQiKSkpICsgIyB4LWF4aXMgbGFiZWwKICB5bGFiKGV4cHJlc3Npb24oLWxvZ1sxMF0oImFkanVzdGVkIHAtdmFsdWUiKSkpICsgIyB5LWF4aXMgbGFiZWwKICBnZW9tX3ZsaW5lKHhpbnRlcmNlcHQgPSAwLCBjb2xvdXIgPSAiYmxhY2siKSArICMgYWRkIGxpbmUgYXQgMAogIGdlb21faGxpbmUoeWludGVyY2VwdCA9IDEuMywgY29sb3VyID0gImJsYWNrIikgKyAjIHAoMC4wNSkgPSAxLjMKICBhbm5vdGF0ZShnZW9tID0gInRleHQiLCAKICAgICAgICAgICBsYWJlbCA9ICJVbnRyZWF0ZWQiLCAKICAgICAgICAgICB4ID0gLTIsIHkgPSA4NSwgCiAgICAgICAgICAgc2l6ZSA9IDcsIGNvbG91ciA9ICJibGFjayIpICsgIyBhZGQgVW50cmVhdGVkIHRleHQKICBhbm5vdGF0ZShnZW9tID0gInRleHQiLCAKICAgICAgICAgICBsYWJlbCA9ICJUcmVhdGVkIiwgCiAgICAgICAgICAgeCA9IDIsIHkgPSA4NSwgCiAgICAgICAgICAgc2l6ZSA9IDcsIGNvbG91ciA9ICJibGFjayIpICsgIyBhZGQgVHJlYXRlZCB0ZXh0CiAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IGMoIlRyZWF0ZWQiID0gIiNFNjRCMzUiLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiVW50cmVhdGVkIiA9ICIjMzE4MmJkIiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm5vbmUiID0gIiM2MzYzNjMiKSkgIyBjaGFuZ2UgY29sb3JzCiAgCiMgUGxvdCBmaWd1cmUKY29sb3JlZApgYGAKCgojIFByb2dyYW1taW5nCgojIyBGb3IgTG9vcHMKCiMjIyBBc3NpZ25pbmcgYSBuZXcgbmFtZSB0byBhIHZhcmlhYmxlIGVhY2ggaXRlcmF0aW9uIHdpdGggYSBmb3IgbG9vcAoKR29hbDogV2l0aCBlYWNoIGl0ZXJhdGlvbiBvZiB0aGUgYEZvcigpYCBsb29wLCBjcmVhdGUgYSBuZXcgdmFyaWFibGUgdGhhdCBhcHBlbmRzIHRoZSBpdGVyYXRvciB2YXJpYWJsZSB0byB0aGUgbmV3IHZhcmlhYmxlLiBUaGlzIGlzIHVzZWZ1bCBmb3IgY3JlYXRpbmcgb2JqZWN0cyBhcyB5b3UgZ28gdGhyb3VnaCBhIGxpc3Qgb3Igc2VxdWVuY2UuIAoKLSBbQ2hhbmdlIHZhcmlhYmxlIG5hbWUgaW4gYEZvcigpYCBsb29wXShodHRwczovL3N0YWNrb3ZlcmZsb3cuY29tL3F1ZXN0aW9ucy8xNjU2Njc5OS9jaGFuZ2UtdmFyaWFibGUtbmFtZS1pbi1mb3ItbG9vcC11c2luZy1yKQotIFtNb3JlIG9uIHRoZSBgYXNzaWduKClgIGZ1bmN0aW9uXShodHRwOi8vc3RhdC5ldGh6LmNoL1ItbWFudWFsL1ItcGF0Y2hlZC9saWJyYXJ5L2Jhc2UvaHRtbC9hc3NpZ24uaHRtbCkKCmBgYHtyIGV2YWw9RkFMU0UsIGVjaG89VFJVRX0KZm9yIChpIGluIHNlcSgxLDYpKXsKIG5hbWUgPC0gcGFzdGUoImRhdGEiLCAiXyIsIGksIHNlcCA9ICIiKQogYXNzaWduKG5hbWUsIHJlYWRfZXhjZWwoIkxlZnRfRmVtYWxlLnhsc3giLCBpKSkKfQpgYGAKCgoqKioKIyBNaXNjZWxsYW5lb3VzCgpbUmVtb3ZlIG9iamVjdHMgYnkgc2VhcmNoIHRlcm1dKGh0dHA6Ly9zdGFja292ZXJmbG93LmNvbS9xdWVzdGlvbnMvMTE2MjQ4ODUvcmVtb3ZlLW11bHRpcGxlLW9iamVjdHMtd2l0aC1ybSkKCglybShsaXN0PWxzKHBhdHRlcm49InRlbXAiKSkKCQpbTWFrZSBhIGxpc3Qgb2YgZGF0YSBmcmFtZXMgYmFzZWQgb24gbmFtaW5nIHBhdHRlcm5dKGh0dHA6Ly9zdGFja292ZXJmbG93LmNvbS9xdWVzdGlvbnMvMTc0OTkwMTMvaG93LWRvLWktbWFrZS1hLWxpc3Qtb2YtZGF0YS1mcmFtZXMpCgoJZGZfbGlzdCA9IG1nZXQobHMocGF0dGVybiA9ICJkZlswLTldIikpCgkjIHRoaXMgd291bGQgbWF0Y2ggYW55IG9iamVjdCB3aXRoICJkZiIgZm9sbG93ZWQgYnkgYSBkaWdpdCBpbiBpdHMgbmFtZQoJIyB5b3UgY2FuIHRlc3Qgd2hhdCBvYmplY3RzIHdpbGwgYmUgZ290IGJ5IGp1c3QgcnVubmluZyB0aGUKCWxzKHBhdHRlcm4gPSAiZGZbMC05XSIpCgkjIHBhcnQgYW5kIGFkanVzdGluZyB0aGUgcGF0dGVybiB1bnRpbCBpdCBnZXRzIHRoZSByaWdodCBvYmplY3RzLgoJCk5vdGU6IFRoZSBjcml0aWNhbCBwaWVjZSBoZXJlIGlzIGBtZ2V0KClgIHdoaWNoIGNhbGxzIHRoZSB2YWx1ZSBvZiBhIG5hbWVkIG9iamVjdCAKCltGaWx0ZXIgYSBsaXN0IGJ5IGNsYXNzIG9mIGxpc3QgaXRlbV0oaHR0cDovL3N0YWNrb3ZlcmZsb3cuY29tL3F1ZXN0aW9ucy8zNjY2MTcwNC9maWx0ZXItdmFsdWVzLW9mLWEtbGlzdC1pbi1yKQoKCUZpbHRlcihmdW5jdGlvbih4KSBpcyh4LCAiZGF0YS5mcmFtZSIpLCBtZ2V0KGxzKHBhdHRlcm49Il9saXN0IikpKSAKClRoaXMgaXMgY29vbC4KIyMgU2VxdWVuY2UgR2VuZXJhdGlvbgoKIyMjIEJpbmFyeSAoMCwxKSBSYW5kb20gQ29sbGVjdGlvbgoKW01ha2UgYmluYXJ5IHZlY3RvciB3aXRoIDAsMV0oaHR0cHM6Ly9zdGFja292ZXJmbG93LmNvbS9xdWVzdGlvbnMvMjYxMzc2NjIvci1nZW5lcmF0ZS1udW1iZXItdmVjdG9yLW9ubHktY29udGFpbnMtMC1hbmQtMS13aXRoLWNlcnRhaW4tbGVuZ3RoKQoKYGBge3IgZXZhbD1UUlVFfQpYIDwtIDEwIApzYW1wbGUoYygwLDEpLCByZXBsYWNlPVRSVUUsIHNpemU9WCkKYGBgCgojIyBBbm5vdGF0ZSBgZ2VvbV9wb2ludGAgb3Igb3RoZXIgcGxvdCBzZWxlY3RpdmVseSB1c2luZyBgYW5ub3RhdGUoKWAKW2dncGxvdCBYWSBzY2F0dGVyIC0gaG93IHRvIGNoYW5nZSBhbHBoYSB0cmFuc3BhcmVuY3kgZm9yIHNlbGVjdCBwb2ludHM/Cl0oaHR0cHM6Ly9zdGFja292ZXJmbG93LmNvbS9xdWVzdGlvbnMvMzI0MjMxNjcvZ2dwbG90LXh5LXNjYXR0ZXItaG93LXRvLWNoYW5nZS1hbHBoYS10cmFuc3BhcmVuY3ktZm9yLXNlbGVjdC1wb2ludHMpCmBgYHtyIGV2YWw9RkFMU0UsIGVjaG89VFJVRX0KCiMjIHlvdSBzdGFydCBoZXJlCmxpYnJhcnkoZ2dwbG90MikKc3BlY2lhbC5wb2ludHMgPC0gc2FtcGxlKDE6biwgNykKIyMgdGhlbiBhZGQgYW5ub3RhdGUgdGV4dApnZ3Bsb3QoZGYsIGFlcyh4PVNlcUlkZW50aXR5TWVhbiwKICAgICAgICAgICAgICAgeT1TZXFJZGVudGl0eVN0ZERldikpICsKICBnZW9tX3BvaW50KGFscGhhPTAuMDUpICsKICBhbm5vdGF0ZSgicG9pbnQiLAogICAgICAgICAgIGRmJFNlcUlkZW50aXR5TWVhbltzcGVjaWFsLnBvaW50c10sCiAgICAgICAgICAgZGYkU2VxSWRlbnRpdHlTdGREZXZbc3BlY2lhbC5wb2ludHNdLAogICAgICAgICAgIGNvbD0icmVkIikgKwogIGFubm90YXRlKCJ0ZXh0IiwKICAgICAgICAgICBkZiRTZXFJZGVudGl0eU1lYW5bc3BlY2lhbC5wb2ludHNdLAogICAgICAgICAgIGRmJFNlcUlkZW50aXR5U3RkRGV2W3NwZWNpYWwucG9pbnRzXSwKICAgICAgICAgICAjdGV4dCB3ZSB3YW50IHRvIGRpc3BsYXkKICAgICAgICAgICBsYWJlbD1yb3VuZChkZiRTZXFJZGVudGl0eVN0ZERldltzcGVjaWFsLnBvaW50c10sMSksCiAgICAgICAgICAgI2FkanVzdCBob3Jpem9udGFsIHBvc2l0aW9uIG9mIHRleHQKICAgICAgICAgICBoanVzdD0tMC4xKQpgYGAKCgojIyMgTGFiZWxzIGluIGdncGxvdDIKCkFkZGluZyBncmVlayBzeW1ib2xzCltTcGVjaWFsIGNoYXJhY3RlcnMgaW4gbGFiZWxzXShodHRwczovL3JzdHVkaW8tcHVicy1zdGF0aWMuczMuYW1hem9uYXdzLmNvbS8xMzYyMzdfMTcwNDAyZTVmMGI1NDU2MWJmNzYwNWJkZWE5ODI2N2EuaHRtbCkKCmBgYHtyIGV2YWw9RiwgZWNobz1UfQojZm9yIHVzZSB3aXRoIGdncGxvdDIKbGFicyh4ID0gZXhwcmVzc2lvbigiQ3Jvc3Mtc2VjdGlvbmFsIEFyZWEiKiIgKCIqbXUqIm0iXnsyfSogIikiKSkgICAjIG1pY3JvbnMuIApgYGAKCgoKIyBSZXNvdXJjZXMgCgotIFtgdGlkeXJgIHR1dG9yaWFsXShodHRwczovL2dpdGh1Yi5jb20vanRyMTMvY29kZWhlbHAvYmxvYi9tYXN0ZXIvUi9nYXRoZXIubWQpCi0gW2BSYCBmb3IgRGF0YSBTY2llbmNlXShodHRwOi8vcjRkcy5oYWQuY28ubnopCi0gW1N0YXQgNTQ1IC0gVUJDXShodHRwOi8vc3RhdDU0NS5jb20vdG9waWNzLmh0bWwpCi0gW1IgQ29va2Jvb2tdKGh0dHA6Ly93d3cuY29va2Jvb2stci5jb20vR3JhcGhzLykKLSBbZ2dwbG90IC0gQ29tcHV0aW5nIGZvciBTb2NpYWwgU2NpZW5jZXNdKGh0dHA6Ly9jZnNzLnVjaGljYWdvLmVkdS9sYWIwMy5odG1sI2hvd190b19yZW1vdmVfdGhlX2xlZ2VuZF9hbmRfY2hhbmdlX3RoZV9sZWdlbmRfcG9zaXRpb24pCi0gW0NyZWF0aW5nIEZ1bmN0aW9uIC0gU29mdHdhcmUgQ2FycGVudHJ5XShodHRwczovL3N3Y2FycGVudHJ5LmdpdGh1Yi5pby9yLW5vdmljZS1pbmZsYW1tYXRpb24vMDItZnVuYy1SLykK