This guide is directed at what I referred to as the “third audience” in an article I published in 2016 (https://www.linkedin.com/pulse/people-analytics-show-me-dont-tell-ben-teusch) - those who have a data background, but little HR expertise, and those in HR with a willingness to work with data, but not much of a quant background. There are already plenty of tutorials to show you how to run a regression or other kinds of predictive models, and I’m not sure that’s what this audience will find most helpful. Please give me feedback on that.
What I’m going to do is focus on showing you how to do things that don’t require too much of a data background, but would be really helpful for the HR folks I know. Those with a stronger data background can use this as a starting point to add in more complex analyses, if they want.
Dataset
The example dataset is based on the HR Employee Attrition and Performance dataset from IBM Watson Analytics (https://www.ibm.com/communities/analytics/watson-analytics-blog/hr-employee-attrition/). I’ve modified it slightly and saved it to my GitHub repository (https://raw.githubusercontent.com/teuschb/hr_data/master/datasets/modified_watson_dataset.csv), which allows me to read in the .csv file directly from the Internet, without needing to download a file.
In practice, your dataset will be your standard HRIS report of headcount, attrition, performance, or whatever data you want to use for a report. That way, each month, quarter, or year, you’ll be able to run this same line of code, replacing the previous file with the name of the most recent dataset that you want to work with. If all the needed data fields have the same names each time, the rest of the code will run without you having to make changes each month. You can even use R to get several standard reports and merge them together quickly, again in an automated way, but here I use just one data file.
This code loads the data from GitHub into R.
mydata <- read.csv("https://raw.githubusercontent.com/teuschb/hr_data/master/datasets/modified_watson_dataset.csv", stringsAsFactors = FALSE)
The ReporteRs package
R isn’t able to write .pptx files by default. What makes this possible is the ReporteRs
package. To learn a lot more about what the package can do, check out http://davidgohel.github.io/ReporteRs/. It allows you to use R to run your analysis, print plots, and export everything to a PowerPoint document in a repeatable, automated way.
ReporteRs
requires rJava
and for Java to be installed on your computer. This code will make sure it’s installed, and will show you the version of Java (it should be at least 1.6).
require(rJava)
system("java -version")
java version "1.6.0_65"
Java(TM) SE Runtime Environment (build 1.6.0_65-b14-468-11M4833)
Java HotSpot(TM) 64-Bit Server VM (build 20.65-b04-468, mixed mode)
The first time you use ReporteRs
, you’ll need to install the package from CRAN, the R package repository. This code checks whether it’s already installed, and installs it if it isn’t.
if (!require('ReporteRs')) {
install.packages("ReporteRs")
}
Analysis
What analysis needs to be done for the report? HR is often guilty of producing reports and dashboards on HR processes that don’t directly help business leaders make better decisions. Good reports should always be produced for a specific purpose. Since I don’t have a specific business problem I’m trying to solve in this example, and because HR is used to reporting on attrition, I’ll prepare some data for a basic attrition report with a few bar charts to visualize the data.
Just know that R is able to produce various types of charts and produce whatever analysis is necessary to help your business. If you have any specific examples in mind you’d like to see, let me know.
Attrition Overview Table
For this example, the first slide will be a table with an overview of attrition.
This code takes the data and uses aggregate
to extract the total number of employees, the number of employees who left, and the attrition rate for this month (calculated as # of employees gone/# of employees at the start of the month
), all by department.
overall_attrition <- as.data.frame(
as.list(aggregate(Attrition ~ Department,
data = mydata,
FUN = function(x) c(
n = length(x),
s = sum(x, na.rm = T),
mn = mean(x, na.rm = T))
)
), stringsAsFactors = F)
Next, I add a row of totals using rbind
.
overall_attrition <- rbind(overall_attrition, c(
0, # to be replaced with the word "Total"
sum(overall_attrition$Attrition.n),
sum(overall_attrition$Attrition.s),
mean(overall_attrition$Attrition.mn))
)
overall_attrition[nrow(overall_attrition),1] <- 'Total'
Before I put the data into a table, I want to add % symbols and to make the column names more presentable. I also change the font size before putting it into a special kind of ReporteRs
table called a vanilla.table
.
Once I make the table, I also format it. I use setZebraStyle
to add some shading, and setFlexTableWidths
to adjust the column widths to fit the text. The final block of code justifies the text to the left or center, and makes some text bold.
# format as percentages, rename columns
overall_attrition$Attrition.mn <- percent(as.numeric(overall_attrition$Attrition.mn))
names(overall_attrition) <- c('Department', 'Headcount', 'Attrition', 'Attrition %')
# Put data into a table
options("ReporteRs-fontsize"=16) #font size for tables
overall_attrition_table <- vanilla.table(overall_attrition) %>%
setZebraStyle(odd = '#eeeeee', even = 'white' ) %>%
setFlexTableWidths(widths = c(4.5, 2.5, 2, 2.5))
overall_attrition_table[,1] = parLeft()
overall_attrition_table[,2:4] = parCenter()
overall_attrition_table[,,to='header'] = parCenter()
overall_attrition_table[4,] = textProperties(font.weight = 'bold' )
overall_attrition_table
Department | Headcount | Attrition | Attrition % |
Human Resources | 63 | 12 | 19.05% |
Research & Development | 961 | 133 | 13.84% |
Sales | 446 | 92 | 20.63% |
Total | 1470 | 237 | 17.84% |
Following that overview, I report attrition by job role, performance, and hire source (or recruiting channel). For each one I’m going to make a plot that shows the turnover, and then create a table that resembles the overall attrition table.
Attrition by job role
This code should look familiar – I’m extracting the total number of employees, the number of employees who left, and the attrition rate for this month by job role.
jobrole_attrition <- as.data.frame(
as.list(aggregate(Attrition ~ JobRole,
data = mydata,
FUN = function(x) c(
n = length(x),
s = sum(x, na.rm = T),
mn = mean(x, na.rm = T))
)
))
names(jobrole_attrition) = c('JobRole', 'Headcount', 'Attrition', 'AttritionRate')
For making graphs, I use the popular ggplot2
package. It’s a really powerful package that allows you to visualize all kinds of data, and customize the resulting plots.
Here, I make a bar chart where each bar represents a JobRole
and the height of the bar represents the attrition rate. I save the graph as jobrole_attrition_plot
so I can use it later.
I do a bit of customization here. I use scale_fill_manual to make the bars blue. Theme_minimal removes a lot of extra chart lines that I prefer not be there. I also change the axis labels into percentages with labels=percent, flip the graph so it’s horizontal with coord_flip, and add custom labels with labs.
(jobrole_attrition_plot <- ggplot(jobrole_attrition,
aes(reorder(JobRole, AttritionRate), AttritionRate)) +
geom_col(aes(fill = '')) +
scale_fill_manual(values = c("#1D243C"),guide=FALSE) +
theme_minimal() +
scale_y_continuous(labels=percent) +
labs(list(y = paste("Attrition in ",month,sep = ""), x = "Job Role",
title = paste("Attrition by Job Role, ", date, sep = ""))) +
theme(panel.grid.minor = element_blank()) +
coord_flip())

I want my table to be sorted from highest to lowest, so first I sort my dataset.
jobrole_attrition <- jobrole_attrition[order(jobrole_attrition$AttritionRate, decreasing = TRUE),]
Although I won’t include it in this table, I can use this information to get the impact of reducing attrition for the job role with the highest attrition. I’ll use this in the recommendations section, and it’s easier to calculate it now.
reduction = .05 # how much could we reduce attrition?
replacement_mult = 1.5 # how much does it cost to replace an employee, as a multiplier of their salary?
top_role <- as.character(jobrole_attrition$JobRole[1]) # job role with highest attrition
top_role_attrition <- jobrole_attrition$AttritionRate[1] # attrition for that role
left_from_top_role <- mydata$JobRole == top_role & mydata$Attrition == 1
salary_lost =
sum(mydata$MonthlyIncome[left_from_top_role]) * 12 * # total annual salary lost
replacement_mult # times replacement cost
impact = salary_lost * reduction # amount we could save
Formatting the table should look familiar.
# format as percentages, rename columns
jobrole_attrition$AttritionRate <- percent(jobrole_attrition$AttritionRate)
names(jobrole_attrition) = c('Job Role', 'Headcount', 'Attrition', 'Attrition %')
# put data into a table
options("ReporteRs-fontsize"=16) #font size for tables
table_widths = c(2.8, 1.2, 1, 1.6)
jobrole_attrition_table <- vanilla.table(jobrole_attrition) %>%
setZebraStyle(odd = '#eeeeee', even = 'white' ) %>%
setFlexTableWidths(widths = table_widths)
jobrole_attrition_table[,2:4] = parCenter()
jobrole_attrition_table[,1] = parLeft()
jobrole_attrition_table[,,to='header'] = parCenter()
jobrole_attrition_table
Job Role | Headcount | Attrition | Attrition % |
Sales Representative | 83 | 33 | 39.8% |
Laboratory Technician | 259 | 62 | 23.9% |
Human Resources | 52 | 12 | 23.1% |
Sales Executive | 326 | 57 | 17.5% |
Research Scientist | 292 | 47 | 16.1% |
Manufacturing Director | 145 | 10 | 6.9% |
Healthcare Representative | 131 | 9 | 6.9% |
Manager | 102 | 5 | 4.9% |
Research Director | 80 | 2 | 2.5% |
Now I’m going to make the same graph and table for performance and hire source. The only difference is I use scale_x_reverse
on performance rating so that the ‘1’ rating is at the top of the graph. I’m also calculating the hire sources that have the highest and lowest attrition rates, for use in the recommendations section.
Attrition by Recruiting Channel
hiresource_attrition <- as.data.frame(
as.list(aggregate(Attrition ~ HireSource, data = mydata,
FUN = function(x) c(
n = length(x),
s = sum(x, na.rm = T),
mn = mean(x, na.rm = T))
)
))
names(hiresource_attrition) = c('HireSource', 'Headcount', 'Attrition', 'AttritionRate')
(hiresource_attrition_plot <- ggplot(hiresource_attrition, aes(reorder(HireSource, AttritionRate), AttritionRate)) +
geom_bar(stat = "identity", aes(fill = '')) +
scale_fill_manual(values = c("#1D243C"),guide=FALSE) +
theme_minimal() +
scale_y_continuous(labels=percent) +
labs(list(y = paste("Attrition in ",month,sep = ""), x = "Job Level",
title = paste("Attrition by Recruiting Channel, ", date, sep = ""))) +
theme(panel.grid.minor = element_blank()) +
coord_flip())

# sort by attrition rate, format as percentages, rename columns
hiresource_attrition <- hiresource_attrition[order(hiresource_attrition$AttritionRate, decreasing = TRUE),]
hiresource_attrition$AttritionRate <- percent(hiresource_attrition$AttritionRate)
names(hiresource_attrition) = c('Recruiting Channel', 'Headcount', 'Attrition', 'Attrition %')
# put data into a table
hiresource_attrition_table <- vanilla.table(hiresource_attrition) %>%
setZebraStyle(odd = '#eeeeee', even = 'white' ) %>%
setFlexTableWidths(widths = table_widths)
hiresource_attrition_table[,1] = parLeft()
hiresource_attrition_table[,2:4] = parCenter()
hiresource_attrition_table[,,to='header'] = parCenter()
hiresource_attrition_table
Recruiting Channel | Headcount | Attrition | Attrition % |
Search Firm | 32 | 8 | 25.00% |
Applied Online | 385 | 81 | 21.04% |
Campus | 257 | 51 | 19.84% |
Referral | 178 | 31 | 17.42% |
# save hire sources with highest and lowest levels of attrition
top_hire <- hiresource_attrition$`Recruiting Channel`[1]
bottom_hire <- hiresource_attrition$`Recruiting Channel`[nrow(hiresource_attrition)]
What’s great about using R to do the graphs is that whenever I need to update this report with new data, all I have to do is change the date, and the rest happens automatically. It takes a little more investment at first, but now all that dreaded Excel work will take almost no time at all!
Creating the Report
Explanation
Once the charts and tables are finished, I need to put them into PowerPoint. I’ve done this many times by copy-pasting the graphs from R into the report, but it’s tedious and hard to be consistent with their size and placement.
Again, for more details about how this works, the guide is on the author’s github page (http://davidgohel.github.io/ReporteRs/articles/powerpoint.html).
First I create a new pptx
document and call it report
. I set the font size to 24, and I like to use slide.layouts(report)
to see which PowerPoint slide layouts are available. The basic idea is to add a slide (addSlide
), choose a layout, and then fill that layout with a title (addTitle
) and text (addParagraph
), a table (addFlexTable
), or a graph (addPlot
). Finally, I use writeDoc
to save the file. I’m saving it into the hr_data folder. You’ll need to make sure that path matches where you want to save it to.
You’ll also see an argument for template - I’m using a PowerPoint template for this report. This means you can build these reports directly on your corporate report template. I have the template saved in the hr_data folder on my machine. You’ll need to update that path with wherever your template is stored.
When you see code like this: par.properties = parProperties(list.style = "unordered", level = 1)
, That’s how ReporteRs can produce indented bullet point lists. Level = 2 would indent the bullets one level, level = 3 would be another level.
If you’ve never seen the %>% operator, you can read it like, “then”. So the following code:
report <- report %>%
addSlide(slide.layout = "Title Slide") %>%
addTitle(paste(date, "Acme Co. Attrition Report"))
can be read, “take report
, then add a title slide, then add a title, and save it as report
.”
Code to produce and publish the report
report <- pptx(title = paste(date, "Acme Co. \nAttrition Report"),
template = '~/hr_data/example_corporate_template.pptx')
options("ReporteRs-fontsize"=24) #font size for text
slide.layouts(report)
[1] "Picture with Caption" "Alternate Section Header" "Two Content"
[4] "Alternate Content with Caption" "Comparison" "Title Slide"
[7] "Title and Content" "Title and Vertical Text" "Section Header"
[10] "Vertical Title and Text" "Content with Caption" "Title Only"
[13] "Blank"
report <- report %>%
addSlide(slide.layout = "Title Slide") %>%
addTitle(paste(date, "Acme Co. Attrition Report"))
report <- report %>%
addSlide(slide.layout = "Title and Content") %>%
addTitle("Table of Contents") %>%
addParagraph( c("Overall Attrition Statistics"),
par.properties = parProperties(list.style = "unordered", level = 1)) %>%
addParagraph( c("Attrition by Subgroups"), append = T,
par.properties = parProperties(list.style = "unordered", level = 1)) %>%
addParagraph( set_of_paragraphs("by Job Role",
"by Performance Rating",
"by Job Level"),
append = T,
par.properties = parProperties(list.style = "unordered", level = 2) )%>%
addParagraph( c("Recommendations"), append = T,
par.properties = parProperties(list.style = "unordered", level = 1))
report <- report %>%
addSlide(slide.layout = "Section Header") %>%
addTitle("Overall Attrition Statistics")
report <- report %>%
addSlide(slide.layout = "Title and Content") %>%
addTitle(paste0("Summary of Attrition in ", date)) %>%
addFlexTable(overall_attrition_table)
report <- report %>%
addSlide(slide.layout = "Section Header") %>%
addTitle("Attrition by Subgroup")
report <- report %>%
addSlide(slide.layout = "Two Content") %>%
addTitle("Which Jobs have the Highest Attrition?") %>%
addPlot(function() print(jobrole_attrition_plot)) %>%
addFlexTable(jobrole_attrition_table)
System font `Calibri` not found. Closest match: `Verdana`
report <- report %>%
addSlide(slide.layout = "Two Content") %>%
addTitle("Are We Keeping Our Top Performers?") %>%
addPlot(function() print(performance_attrition_plot)) %>%
addFlexTable(performance_attrition_table)
report <- report %>%
addSlide(slide.layout = "Two Content") %>%
addTitle("Which Hiring Channels have High Turnover?") %>%
addPlot(function() print(hiresource_attrition_plot)) %>%
addFlexTable(hiresource_attrition_table)
report <- report %>%
addSlide(slide.layout = "Title and Content") %>%
addTitle("Key Insights") %>%
addParagraph( c(paste0("The job group with the highest attrition was ", top_role,
" with an attrition rate of ",as.character(jobrole_attrition$`Attrition %`[1]),".")),
par.properties = parProperties(list.style = "unordered", level = 1)) %>%
addParagraph( c(paste0("Additional focus should be placed on retaining ", top_role,
"s.")),
append = T,
par.properties = parProperties(list.style = "unordered", level = 2)) %>%
addParagraph( c(paste0("We estimate that replacing an employee costs ", replacement_mult,
"x their annual salary. Reducing attrition by ", percent(reduction),
" in ", month, " could have saved ", dollar(round(impact,0)), ".")),
append = T,
par.properties = parProperties(list.style = "unordered", level = 2)) %>%
addParagraph( c(paste0("Our ", hiresource_attrition$`Recruiting Channel`[1],
" hires have the highest attrition rate. The channel with the lowest attrition rate in ",
month, " was the ",
hiresource_attrition$`Recruiting Channel`[nrow(hiresource_attrition)], " channel.")),
append = T,
par.properties = parProperties(list.style = "unordered", level = 1))
writeDoc(report, paste0("~/hr_data/examples/",date," Attrition Report.pptx"))
And there you have it! The final report can be seen on SlideShare (http://www.slideshare.net/BenTeusch/automated-attrition-report-using-r). Again, if you’re interested in starting with a simple version of this code, check out http://rpubs.com/teuschb/making_ppt_slides_simple.
I hope this guide can be helpful for analytics teams, and especially people analytics teams or HR reporting teams, who would like to reduce the amount of time they spend on routine reports. Well-considered reporting is still a pillar of effective people analytics, but the best teams reduce the time they spend on it as much as possible, so they can work on more advanced analytics. If you find other helpful guides for people analytics, or would like to see something specific, please let me know in the comments!
LS0tCnRpdGxlOiAiTWFraW5nIFBvd2VyUG9pbnQgU2xpZGVzIHdpdGggUiIKb3V0cHV0OiBodG1sX25vdGVib29rCi0tLQpUaGlzIGd1aWRlIGlzIGRpcmVjdGVkIGF0IHdoYXQgSSByZWZlcnJlZCB0byBhcyB0aGUgInRoaXJkIGF1ZGllbmNlIiBpbiBhbiBhcnRpY2xlIEkgcHVibGlzaGVkIGluIDIwMTYgKGh0dHBzOi8vd3d3LmxpbmtlZGluLmNvbS9wdWxzZS9wZW9wbGUtYW5hbHl0aWNzLXNob3ctbWUtZG9udC10ZWxsLWJlbi10ZXVzY2gpIC0gdGhvc2Ugd2hvIGhhdmUgYSBkYXRhIGJhY2tncm91bmQsIGJ1dCBsaXR0bGUgSFIgZXhwZXJ0aXNlLCBhbmQgdGhvc2UgaW4gSFIgd2l0aCBhIHdpbGxpbmduZXNzIHRvIHdvcmsgd2l0aCBkYXRhLCBidXQgbm90IG11Y2ggb2YgYSBxdWFudCBiYWNrZ3JvdW5kLiBUaGVyZSBhcmUgYWxyZWFkeSBwbGVudHkgb2YgdHV0b3JpYWxzIHRvIHNob3cgeW91IGhvdyB0byBydW4gYSByZWdyZXNzaW9uIG9yIG90aGVyIGtpbmRzIG9mIHByZWRpY3RpdmUgbW9kZWxzLCBhbmQgSSdtIG5vdCBzdXJlIHRoYXQncyB3aGF0IHRoaXMgYXVkaWVuY2Ugd2lsbCBmaW5kIG1vc3QgaGVscGZ1bC4gUGxlYXNlIGdpdmUgbWUgZmVlZGJhY2sgb24gdGhhdC4gCgpXaGF0IEknbSBnb2luZyB0byBkbyBpcyBmb2N1cyBvbiBzaG93aW5nIHlvdSBob3cgdG8gZG8gdGhpbmdzIHRoYXQgZG9uJ3QgcmVxdWlyZSB0b28gbXVjaCBvZiBhIGRhdGEgYmFja2dyb3VuZCwgYnV0IHdvdWxkIGJlIHJlYWxseSBoZWxwZnVsIGZvciB0aGUgSFIgZm9sa3MgSSBrbm93LiBUaG9zZSB3aXRoIGEgc3Ryb25nZXIgZGF0YSBiYWNrZ3JvdW5kIGNhbiB1c2UgdGhpcyBhcyBhIHN0YXJ0aW5nIHBvaW50IHRvIGFkZCBpbiBtb3JlIGNvbXBsZXggYW5hbHlzZXMsIGlmIHRoZXkgd2FudC4KCiMjIE1ha2luZyBIUiBSZXBvcnRzCk5vIG1hdHRlciBob3cgbWF0dXJlIHBlb3BsZSBhbmFseXRpY3MgaXMgYXQgeW91ciBvcmdhbml6YXRpb24sIGF0IHNvbWUgcG9pbnQgeW91J3JlIGdvaW5nIHRvIG5lZWQgdG8gbWFrZSBzb21lIHJlcG9ydHMuIFRoaXMgaXMgZXNwZWNpYWxseSB0cnVlIGlmIHlvdSdyZSBhbiBIUkJQIHdpdGggbGltaXRlZCB0ZWNobmljYWwgc3VwcG9ydCAtLSB5b3UgbWlnaHQgZXZlbiBiZSBtYWtpbmcgdGhlc2UgcmVwb3J0cyB5b3Vyc2VsZiEgVGhlc2UgcmVwb3J0cywgb2Z0ZW4gZG9uZSBtb250aGx5LCBjYW4gdGFrZSBob3VycyBvZiBWTE9PS1VQcyBhbmQgY29weS1wYXN0aW5nIGluIEV4Y2VsLCB0aGVuIGFub3RoZXIgd2hpbGUgdG8gcHV0IHRoZSByaWdodCBudW1iZXJzIGludG8gYSBQb3dlclBvaW50IHRlbXBsYXRlLiBUb2RheSBJJ20gZ29pbmcgdG8gc2hvdyB5b3UgaG93IHRvIGF1dG9tYXRlIHRob3NlIHJlcG9ydHMgdXNpbmcgUiwgc28geW91IGNhbiBzYXZlIGEgYnVuY2ggb2YgdGltZSBkb3duIHRoZSByb2FkLiBBbmRyZXcgTWFycml0dCByZWNlbnRseSB3cm90ZSBhYm91dCBoaXMgcHJvY2VzcyBmb3IgYXV0b21hdGluZyBxdWFsaXR5IHJlcG9ydHMgKGh0dHBzOi8vd3d3LmxpbmtlZGluLmNvbS9wdWxzZS9tYW5hZ2VyLWxldmVsLXJlcG9ydGluZy1ob3ctd2UtYXV0b21hdGUtcHJvY2Vzcy1hbmRyZXctbWFycml0dCksIGFuZCBoaXMgYWR2aWNlIGlzIGdyZWF0LiBJJ2xsIGJlIHNob3dpbmcgeW91IGxlc3Mgc3RyYXRlZ3kgYW5kIG1vcmUgdGFjdGljcyBhYm91dCBob3cgdG8gYWN0dWFsbHkgbWFrZSB0aGlzIHJlcG9ydCAoaHR0cDovL3d3dy5zbGlkZXNoYXJlLm5ldC9CZW5UZXVzY2gvYXV0b21hdGVkLWF0dHJpdGlvbi1yZXBvcnQtdXNpbmctcikgaW4gUi4gCgojIyBHZXR0aW5nIFN0YXJ0ZWQgd2l0aCBSCklmIHlvdSd2ZSBuZXZlciB1c2VkIFIgYmVmb3JlLCBjaGVjayBvdXQgUmljaGFyZCBSb3Nlbm93J3MgSW50cm8gdG8gUiBwb3N0IChodHRwczovL3d3dy5saW5rZWRpbi5jb20vcHVsc2UvaHItYW5hbHl0aWNzLXN0YXJ0ZXIta2l0LXBhcnQtMi1pbnRyby1yLXJpY2hhcmQtcm9zZW5vdy1wbXApLCBhbmQgc3BlY2lmaWNhbGx5IHlvdSBjYW4gZG93bmxvYWQgUiAoaHR0cHM6Ly9taXJyb3IubGFzLmlhc3RhdGUuZWR1L0NSQU4vKSBhbmQgUlN0dWRpbyAoaHR0cHM6Ly93d3cucnN0dWRpby5jb20vcHJvZHVjdHMvcnN0dWRpby9kb3dubG9hZDMvKS4gVGhlcmUgYXJlIGxvdHMgb2YgaGVscGZ1bCB0aXBzIG9ubGluZSBhYm91dCBnZXR0aW5nIHN0YXJ0ZWQgd2l0aCBSLCBhbmQgSSdtIHdyaXRpbmcgdGhpcyBwb3N0IHdpdGggdGhlIGFzc3VtcHRpb24gdGhhdCB5b3UgaGF2ZSBSU3R1ZGlvIGluc3RhbGxlZCBhbmQgeW91J3JlIGFibGUgdG8gcnVuIGNvZGUgdGhhdCBJIHByb3ZpZGUuCgojIyBDb2RlIGZvciB0aGlzIGFydGljbGUKVGhlIFIgbm90ZWJvb2sgdXNlZCB0byBwcm9kdWNlIHRoaXMgYXJ0aWNsZSBpcyBmb3VuZCBvbiBteSBHaXRIdWIgcmVwb3NpdG9yeSBhdCAgaHR0cHM6Ly9naXRodWIuY29tL3RldXNjaGIvaHJfZGF0YS9ibG9iL21hc3Rlci9hbmFseXNlcy9hdXRvbWF0aW5nX3JlcG9ydF9zbGlkZXMuUm1kLiBJZiB5b3UganVzdCB3YW50IHRoZSBzY3JpcHQgd2l0aCBjb2RlIG9ubHksIHRoYXQncyBmb3VuZCBhdCBodHRwczovL2dpdGh1Yi5jb20vdGV1c2NoYi9ocl9kYXRhL2Jsb2IvbWFzdGVyL2FuYWx5c2VzL2F1dG9tYXRpbmdfcmVwb3J0X3NsaWRlcy5SLiBUaGVyZSBpcyBhbHNvIGEgc2ltcGxpZmllZCB2ZXJzaW9uIG9mIHRoaXMgY29kZSBhdCBodHRwczovL2dpdGh1Yi5jb20vdGV1c2NoYi9ocl9kYXRhL2Jsb2IvbWFzdGVyL2FuYWx5c2VzL2F1dG9tYXRpbmdfcmVwb3J0X3NsaWRlc19zaW1wbGUuUm1kIHdoaWNoIHByb2R1Y2VzIGEgUG93ZXJQb2ludCByZXBvcnQgdXNpbmcgbXVjaCBzaW1wbGVyIGNvZGUsIGJ1dCBkb2Vzbid0IGxvb2sgcXVpdGUgYXMgbmljZS4KClRocm91Z2hvdXQgdGhpcyBleGFtcGxlLCBJJ3ZlIHVzZWQgbW9zdGx5IGJhc2UgUiBmdW5jdGlvbnMgdG8gbWFuaXB1bGF0ZSBkYXRhLiBIb3dldmVyLCBJIGhpZ2hseSByZWNvbW1lbmQgZHBseXIsIGFuZCB0aGUgdGlkeXZlcnNlIGluIGdlbmVyYWwuIENoZWNrIHRoYXQgb3V0IGhlcmU6IGh0dHA6Ly90aWR5dmVyc2Uub3JnLwoKIyMgRGF0YXNldApUaGUgZXhhbXBsZSBkYXRhc2V0IGlzIGJhc2VkIG9uIHRoZSBIUiBFbXBsb3llZSBBdHRyaXRpb24gYW5kIFBlcmZvcm1hbmNlIGRhdGFzZXQgZnJvbSBJQk0gV2F0c29uIEFuYWx5dGljcyAoaHR0cHM6Ly93d3cuaWJtLmNvbS9jb21tdW5pdGllcy9hbmFseXRpY3Mvd2F0c29uLWFuYWx5dGljcy1ibG9nL2hyLWVtcGxveWVlLWF0dHJpdGlvbi8pLiBJJ3ZlIG1vZGlmaWVkIGl0IHNsaWdodGx5IGFuZCBzYXZlZCBpdCB0byBteSBHaXRIdWIgcmVwb3NpdG9yeSAoaHR0cHM6Ly9yYXcuZ2l0aHVidXNlcmNvbnRlbnQuY29tL3RldXNjaGIvaHJfZGF0YS9tYXN0ZXIvZGF0YXNldHMvbW9kaWZpZWRfd2F0c29uX2RhdGFzZXQuY3N2KSwgd2hpY2ggYWxsb3dzIG1lIHRvIHJlYWQgaW4gdGhlIC5jc3YgZmlsZSBkaXJlY3RseSBmcm9tIHRoZSBJbnRlcm5ldCwgd2l0aG91dCBuZWVkaW5nIHRvIGRvd25sb2FkIGEgZmlsZS4KCkluIHByYWN0aWNlLCB5b3VyIGRhdGFzZXQgd2lsbCBiZSB5b3VyIHN0YW5kYXJkIEhSSVMgcmVwb3J0IG9mIGhlYWRjb3VudCwgYXR0cml0aW9uLCBwZXJmb3JtYW5jZSwgb3Igd2hhdGV2ZXIgZGF0YSB5b3Ugd2FudCB0byB1c2UgZm9yIGEgcmVwb3J0LiBUaGF0IHdheSwgZWFjaCBtb250aCwgcXVhcnRlciwgb3IgeWVhciwgeW91J2xsIGJlIGFibGUgdG8gcnVuIHRoaXMgc2FtZSBsaW5lIG9mIGNvZGUsIHJlcGxhY2luZyB0aGUgcHJldmlvdXMgZmlsZSB3aXRoIHRoZSBuYW1lIG9mIHRoZSBtb3N0IHJlY2VudCBkYXRhc2V0IHRoYXQgeW91IHdhbnQgdG8gd29yayB3aXRoLiBJZiBhbGwgdGhlIG5lZWRlZCBkYXRhIGZpZWxkcyBoYXZlIHRoZSBzYW1lIG5hbWVzIGVhY2ggdGltZSwgdGhlIHJlc3Qgb2YgdGhlIGNvZGUgd2lsbCBydW4gd2l0aG91dCB5b3UgaGF2aW5nIHRvIG1ha2UgY2hhbmdlcyBlYWNoIG1vbnRoLiBZb3UgY2FuIGV2ZW4gdXNlIFIgdG8gZ2V0IHNldmVyYWwgc3RhbmRhcmQgcmVwb3J0cyBhbmQgbWVyZ2UgdGhlbSB0b2dldGhlciBxdWlja2x5LCBhZ2FpbiBpbiBhbiBhdXRvbWF0ZWQgd2F5LCBidXQgaGVyZSBJIHVzZSBqdXN0IG9uZSBkYXRhIGZpbGUuCgpUaGlzIGNvZGUgbG9hZHMgdGhlIGRhdGEgZnJvbSBHaXRIdWIgaW50byBSLgoKYGBge3J9Cm15ZGF0YSA8LSByZWFkLmNzdigiaHR0cHM6Ly9yYXcuZ2l0aHVidXNlcmNvbnRlbnQuY29tL3RldXNjaGIvaHJfZGF0YS9tYXN0ZXIvZGF0YXNldHMvbW9kaWZpZWRfd2F0c29uX2RhdGFzZXQuY3N2Iiwgc3RyaW5nc0FzRmFjdG9ycyA9IEZBTFNFKQpgYGAKCiMjIFNldCBSZXBvcnQgUGFyYW1ldGVycwpUaGUgb25lIHRoaW5nIEkgZG8gbWFudWFsbHkgdXBkYXRlIGVhY2ggbW9udGggaXMgdGhlIGRhdGUgb2YgdGhlIHJlcG9ydC4gQnkgc2V0dGluZyBpdCBvbmNlIGF0IHRoZSB0b3AsIEkgY2FuIHJlZmVyZW5jZSBpdCB0aHJvdWdob3V0IHRoZSBzY3JpcHQgd2l0aG91dCB3b3JyeWluZyBhYm91dCBhbGwgdGhlIHBsYWNlcyBJIG1heSBoYXZlIHVzZWQgaXQuCgpgYGB7cn0KbW9udGggPSAiTWF5IgpkYXRlID0gIk1heSAyMDE3IgpgYGAKCiMjIFRoZSBSZXBvcnRlUnMgcGFja2FnZQpSIGlzbid0IGFibGUgdG8gd3JpdGUgLnBwdHggZmlsZXMgYnkgZGVmYXVsdC4gV2hhdCBtYWtlcyB0aGlzIHBvc3NpYmxlIGlzIHRoZSBgUmVwb3J0ZVJzYCBwYWNrYWdlLiBUbyBsZWFybiBhIGxvdCBtb3JlIGFib3V0IHdoYXQgdGhlIHBhY2thZ2UgY2FuIGRvLCBjaGVjayBvdXQgaHR0cDovL2RhdmlkZ29oZWwuZ2l0aHViLmlvL1JlcG9ydGVScy8uIEl0IGFsbG93cyB5b3UgdG8gdXNlIFIgdG8gcnVuIHlvdXIgYW5hbHlzaXMsIHByaW50IHBsb3RzLCBhbmQgZXhwb3J0IGV2ZXJ5dGhpbmcgdG8gYSBQb3dlclBvaW50IGRvY3VtZW50IGluIGEgcmVwZWF0YWJsZSwgYXV0b21hdGVkIHdheS4KCmBSZXBvcnRlUnNgIHJlcXVpcmVzIGBySmF2YWAgYW5kIGZvciBKYXZhIHRvIGJlIGluc3RhbGxlZCBvbiB5b3VyIGNvbXB1dGVyLiBUaGlzIGNvZGUgd2lsbCBtYWtlIHN1cmUgaXQncyBpbnN0YWxsZWQsIGFuZCB3aWxsIHNob3cgeW91IHRoZSB2ZXJzaW9uIG9mIEphdmEgKGl0IHNob3VsZCBiZSBhdCBsZWFzdCAxLjYpLgpgYGB7cn0KcmVxdWlyZShySmF2YSkKc3lzdGVtKCJqYXZhIC12ZXJzaW9uIikKYGBgClRoZSBmaXJzdCB0aW1lIHlvdSB1c2UgYFJlcG9ydGVSc2AsIHlvdSdsbCBuZWVkIHRvIGluc3RhbGwgdGhlIHBhY2thZ2UgZnJvbSBDUkFOLCB0aGUgUiBwYWNrYWdlIHJlcG9zaXRvcnkuIFRoaXMgY29kZSBjaGVja3Mgd2hldGhlciBpdCdzIGFscmVhZHkgaW5zdGFsbGVkLCBhbmQgaW5zdGFsbHMgaXQgaWYgaXQgaXNuJ3QuCmBgYHtyfQppZiAoIXJlcXVpcmUoJ1JlcG9ydGVScycpKSB7CiAgaW5zdGFsbC5wYWNrYWdlcygiUmVwb3J0ZVJzIikKfQpgYGAKCiMjIExpYnJhcmllcwpCZWZvcmUgd2UgY2FuIGdldCBzdGFydGVkLCB3ZSBoYXZlIHRvIGxvYWQgaW4gYSBmZXcgb3RoZXIgcGFja2FnZXMgSSBsaWtlIHRvIHVzZS4gWW91J2xsIHNlZSB0aGF0IGBSZXBvcnRlUnNgIGlzIG9uIHRoZSBsaXN0IC0gcnVubmluZyBgaW5zdGFsbC5wYWNrYWdlcygnUmVwb3J0ZVJzJylgIGdldHMgdGhlIHBhY2thZ2Ugb250byBvdXIgY29tcHV0ZXIsIGFuZCBgbGlicmFyeShSZXBvcnRlUnMpYCB0ZWxscyBSIHRoYXQgd2Ugd2FudCB0byB1c2UgdGhhdCBwYWNrYWdlIHJpZ2h0IG5vdy4gCgpJZiB5b3Ugc2VlIGEgcGFja2FnZSBuYW1lIHlvdSBkb24ndCByZWNvZ25pemUsIG1ha2Ugc3VyZSB5b3UgcnVuIGluc3RhbGwucGFja2FnZXMgZm9yIGl0LiBGb3IgZXhhbXBsZSwgYGluc3RhbGwucGFja2FnZXMoJ2dncGxvdDInKWAuCmBgYHtyfQpsaWJyYXJ5KFJlcG9ydGVScykKbGlicmFyeShnZ3Bsb3QyKSAgICAgIyBmb3IgcGxvdHRpbmcgZ3JhcGhzCmxpYnJhcnkoc2NhbGVzKSAgICAgICMgZm9yIGZvcm1hdHRpbmcgbnVtYmVycwpsaWJyYXJ5KG1hZ3JpdHRyKSAgICAjIGZvciB0aGUgJT4lIG9wZXJhdG9yCmBgYAoKIyMgQW5hbHlzaXMKV2hhdCBhbmFseXNpcyBuZWVkcyB0byBiZSBkb25lIGZvciB0aGUgcmVwb3J0PyBIUiBpcyBvZnRlbiBndWlsdHkgb2YgcHJvZHVjaW5nIHJlcG9ydHMgYW5kIGRhc2hib2FyZHMgb24gSFIgcHJvY2Vzc2VzIHRoYXQgZG9uJ3QgZGlyZWN0bHkgaGVscCBidXNpbmVzcyBsZWFkZXJzIG1ha2UgYmV0dGVyIGRlY2lzaW9ucy4gR29vZCByZXBvcnRzIHNob3VsZCBhbHdheXMgYmUgcHJvZHVjZWQgZm9yIGEgc3BlY2lmaWMgcHVycG9zZS4gU2luY2UgSSBkb24ndCBoYXZlIGEgc3BlY2lmaWMgYnVzaW5lc3MgcHJvYmxlbSBJJ20gdHJ5aW5nIHRvIHNvbHZlIGluIHRoaXMgZXhhbXBsZSwgYW5kIGJlY2F1c2UgSFIgaXMgdXNlZCB0byByZXBvcnRpbmcgb24gYXR0cml0aW9uLCBJJ2xsIHByZXBhcmUgc29tZSBkYXRhIGZvciBhIGJhc2ljIGF0dHJpdGlvbiByZXBvcnQgd2l0aCBhIGZldyBiYXIgY2hhcnRzIHRvIHZpc3VhbGl6ZSB0aGUgZGF0YS4gCgpKdXN0IGtub3cgdGhhdCBSIGlzIGFibGUgdG8gcHJvZHVjZSB2YXJpb3VzIHR5cGVzIG9mIGNoYXJ0cyBhbmQgcHJvZHVjZSB3aGF0ZXZlciBhbmFseXNpcyBpcyBuZWNlc3NhcnkgdG8gaGVscCB5b3VyIGJ1c2luZXNzLiBJZiB5b3UgaGF2ZSBhbnkgc3BlY2lmaWMgZXhhbXBsZXMgaW4gbWluZCB5b3UnZCBsaWtlIHRvIHNlZSwgbGV0IG1lIGtub3cuCgojIyMjIEF0dHJpdGlvbiBPdmVydmlldyBUYWJsZQpGb3IgdGhpcyBleGFtcGxlLCB0aGUgZmlyc3Qgc2xpZGUgd2lsbCBiZSBhIHRhYmxlIHdpdGggYW4gb3ZlcnZpZXcgb2YgYXR0cml0aW9uLgoKVGhpcyBjb2RlIHRha2VzIHRoZSBkYXRhIGFuZCB1c2VzIGBhZ2dyZWdhdGVgIHRvIGV4dHJhY3QgdGhlIHRvdGFsIG51bWJlciBvZiBlbXBsb3llZXMsIHRoZSBudW1iZXIgb2YgZW1wbG95ZWVzIHdobyBsZWZ0LCBhbmQgdGhlIGF0dHJpdGlvbiByYXRlIGZvciB0aGlzIG1vbnRoIChjYWxjdWxhdGVkIGFzIGAjIG9mIGVtcGxveWVlcyBnb25lLyMgb2YgZW1wbG95ZWVzIGF0IHRoZSBzdGFydCBvZiB0aGUgbW9udGhgKSwgYWxsIGJ5IGRlcGFydG1lbnQuCgpgYGB7cn0Kb3ZlcmFsbF9hdHRyaXRpb24gPC0gYXMuZGF0YS5mcmFtZSgKICBhcy5saXN0KGFnZ3JlZ2F0ZShBdHRyaXRpb24gfiBEZXBhcnRtZW50LAogICAgICAgICAgICAgICAgICAgIGRhdGEgPSBteWRhdGEsCiAgICAgICAgICAgICAgICAgICAgRlVOID0gZnVuY3Rpb24oeCkgYygKICAgICAgICAgICAgICAgICAgICAgIG4gPSBsZW5ndGgoeCksCiAgICAgICAgICAgICAgICAgICAgICBzID0gc3VtKHgsIG5hLnJtID0gVCksCiAgICAgICAgICAgICAgICAgICAgICBtbiA9IG1lYW4oeCwgbmEucm0gPSBUKSkKICAgICAgICAgICAgICAgICAgICApCiAgICAgICAgICApLCBzdHJpbmdzQXNGYWN0b3JzID0gRikKYGBgCgpOZXh0LCBJIGFkZCBhIHJvdyBvZiB0b3RhbHMgdXNpbmcgYHJiaW5kYC4KYGBge3J9Cm92ZXJhbGxfYXR0cml0aW9uIDwtIHJiaW5kKG92ZXJhbGxfYXR0cml0aW9uLCBjKAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIDAsICMgdG8gYmUgcmVwbGFjZWQgd2l0aCB0aGUgd29yZCAiVG90YWwiCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc3VtKG92ZXJhbGxfYXR0cml0aW9uJEF0dHJpdGlvbi5uKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzdW0ob3ZlcmFsbF9hdHRyaXRpb24kQXR0cml0aW9uLnMpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIG1lYW4ob3ZlcmFsbF9hdHRyaXRpb24kQXR0cml0aW9uLm1uKSkKICAgICAgICAgICAgICAgICAgICAgICAgICAgKQpvdmVyYWxsX2F0dHJpdGlvbltucm93KG92ZXJhbGxfYXR0cml0aW9uKSwxXSA8LSAnVG90YWwnCmBgYAoKQmVmb3JlIEkgcHV0IHRoZSBkYXRhIGludG8gYSB0YWJsZSwgSSB3YW50IHRvIGFkZCAlIHN5bWJvbHMgYW5kIHRvIG1ha2UgdGhlIGNvbHVtbiBuYW1lcyBtb3JlIHByZXNlbnRhYmxlLiBJIGFsc28gY2hhbmdlIHRoZSBmb250IHNpemUgYmVmb3JlIHB1dHRpbmcgaXQgaW50byBhIHNwZWNpYWwga2luZCBvZiBgUmVwb3J0ZVJzYCB0YWJsZSBjYWxsZWQgYSBgdmFuaWxsYS50YWJsZWAuCgpPbmNlIEkgbWFrZSB0aGUgdGFibGUsIEkgYWxzbyBmb3JtYXQgaXQuIEkgdXNlIGBzZXRaZWJyYVN0eWxlYCB0byBhZGQgc29tZSBzaGFkaW5nLCBhbmQgYHNldEZsZXhUYWJsZVdpZHRoc2AgdG8gYWRqdXN0IHRoZSBjb2x1bW4gd2lkdGhzIHRvIGZpdCB0aGUgdGV4dC4gVGhlIGZpbmFsIGJsb2NrIG9mIGNvZGUganVzdGlmaWVzIHRoZSB0ZXh0IHRvIHRoZSBsZWZ0IG9yIGNlbnRlciwgYW5kIG1ha2VzIHNvbWUgdGV4dCBib2xkLgoKYGBge3J9CiMgZm9ybWF0IGFzIHBlcmNlbnRhZ2VzLCByZW5hbWUgY29sdW1ucwpvdmVyYWxsX2F0dHJpdGlvbiRBdHRyaXRpb24ubW4gPC0gcGVyY2VudChhcy5udW1lcmljKG92ZXJhbGxfYXR0cml0aW9uJEF0dHJpdGlvbi5tbikpCm5hbWVzKG92ZXJhbGxfYXR0cml0aW9uKSA8LSBjKCdEZXBhcnRtZW50JywgJ0hlYWRjb3VudCcsICdBdHRyaXRpb24nLCAnQXR0cml0aW9uICUnKQoKIyBQdXQgZGF0YSBpbnRvIGEgdGFibGUKb3B0aW9ucygiUmVwb3J0ZVJzLWZvbnRzaXplIj0xNikgI2ZvbnQgc2l6ZSBmb3IgdGFibGVzCm92ZXJhbGxfYXR0cml0aW9uX3RhYmxlIDwtIHZhbmlsbGEudGFibGUob3ZlcmFsbF9hdHRyaXRpb24pICU+JQogIHNldFplYnJhU3R5bGUob2RkID0gJyNlZWVlZWUnLCBldmVuID0gJ3doaXRlJyApICU+JQogIHNldEZsZXhUYWJsZVdpZHRocyh3aWR0aHMgPSBjKDQuNSwgMi41LCAyLCAyLjUpKSAKCm92ZXJhbGxfYXR0cml0aW9uX3RhYmxlWywxXSA9IHBhckxlZnQoKQpvdmVyYWxsX2F0dHJpdGlvbl90YWJsZVssMjo0XSA9IHBhckNlbnRlcigpCm92ZXJhbGxfYXR0cml0aW9uX3RhYmxlWywsdG89J2hlYWRlciddID0gcGFyQ2VudGVyKCkKb3ZlcmFsbF9hdHRyaXRpb25fdGFibGVbNCxdID0gdGV4dFByb3BlcnRpZXMoZm9udC53ZWlnaHQgPSAnYm9sZCcgKQpvdmVyYWxsX2F0dHJpdGlvbl90YWJsZQpgYGAKCkZvbGxvd2luZyB0aGF0IG92ZXJ2aWV3LCBJIHJlcG9ydCBhdHRyaXRpb24gYnkgam9iIHJvbGUsIHBlcmZvcm1hbmNlLCBhbmQgaGlyZSBzb3VyY2UgKG9yIHJlY3J1aXRpbmcgY2hhbm5lbCkuIEZvciBlYWNoIG9uZSBJJ20gZ29pbmcgdG8gbWFrZSBhIHBsb3QgdGhhdCBzaG93cyB0aGUgdHVybm92ZXIsIGFuZCB0aGVuIGNyZWF0ZSBhIHRhYmxlIHRoYXQgcmVzZW1ibGVzIHRoZSBvdmVyYWxsIGF0dHJpdGlvbiB0YWJsZS4KCiMjIyMgQXR0cml0aW9uIGJ5IGpvYiByb2xlClRoaXMgY29kZSBzaG91bGQgbG9vayBmYW1pbGlhciAtLSBJJ20gZXh0cmFjdGluZyB0aGUgdG90YWwgbnVtYmVyIG9mIGVtcGxveWVlcywgdGhlIG51bWJlciBvZiBlbXBsb3llZXMgd2hvIGxlZnQsIGFuZCB0aGUgYXR0cml0aW9uIHJhdGUgZm9yIHRoaXMgbW9udGggYnkgam9iIHJvbGUuCmBgYHtyfQpqb2Jyb2xlX2F0dHJpdGlvbiA8LSBhcy5kYXRhLmZyYW1lKAogIGFzLmxpc3QoYWdncmVnYXRlKEF0dHJpdGlvbiB+IEpvYlJvbGUsCiAgICAgICAgICAgICAgICAgICAgZGF0YSA9IG15ZGF0YSwKICAgICAgICAgICAgICAgICAgICBGVU4gPSBmdW5jdGlvbih4KSBjKAogICAgICAgICAgICAgICAgICAgICAgbiA9IGxlbmd0aCh4KSwKICAgICAgICAgICAgICAgICAgICAgIHMgPSBzdW0oeCwgbmEucm0gPSBUKSwKICAgICAgICAgICAgICAgICAgICAgIG1uID0gbWVhbih4LCBuYS5ybSA9IFQpKQogICAgICAgICAgICAgICAgICAgICkKICAgICAgICAgICkpCgpuYW1lcyhqb2Jyb2xlX2F0dHJpdGlvbikgPSBjKCdKb2JSb2xlJywgJ0hlYWRjb3VudCcsICdBdHRyaXRpb24nLCAnQXR0cml0aW9uUmF0ZScpCmBgYAoKRm9yIG1ha2luZyBncmFwaHMsIEkgdXNlIHRoZSBwb3B1bGFyIGBnZ3Bsb3QyYCBwYWNrYWdlLiBJdCdzIGEgcmVhbGx5IHBvd2VyZnVsIHBhY2thZ2UgdGhhdCBhbGxvd3MgeW91IHRvIHZpc3VhbGl6ZSBhbGwga2luZHMgb2YgZGF0YSwgYW5kIGN1c3RvbWl6ZSB0aGUgcmVzdWx0aW5nIHBsb3RzLgoKSGVyZSwgSSBtYWtlIGEgYmFyIGNoYXJ0IHdoZXJlIGVhY2ggYmFyIHJlcHJlc2VudHMgYSBgSm9iUm9sZWAgYW5kIHRoZSBoZWlnaHQgb2YgdGhlIGJhciByZXByZXNlbnRzIHRoZSBhdHRyaXRpb24gcmF0ZS4gSSBzYXZlIHRoZSBncmFwaCBhcyBgam9icm9sZV9hdHRyaXRpb25fcGxvdGAgc28gSSBjYW4gdXNlIGl0IGxhdGVyLgoKSSBkbyBhIGJpdCBvZiBjdXN0b21pemF0aW9uIGhlcmUuIEkgdXNlIHNjYWxlX2ZpbGxfbWFudWFsIHRvIG1ha2UgdGhlIGJhcnMgYmx1ZS4gVGhlbWVfbWluaW1hbCByZW1vdmVzIGEgbG90IG9mIGV4dHJhIGNoYXJ0IGxpbmVzIHRoYXQgSSBwcmVmZXIgbm90IGJlIHRoZXJlLiBJIGFsc28gY2hhbmdlIHRoZSBheGlzIGxhYmVscyBpbnRvIHBlcmNlbnRhZ2VzIHdpdGggbGFiZWxzPXBlcmNlbnQsIGZsaXAgdGhlIGdyYXBoIHNvIGl04oCZcyBob3Jpem9udGFsIHdpdGggY29vcmRfZmxpcCwgYW5kIGFkZCBjdXN0b20gbGFiZWxzIHdpdGggbGFicy4KYGBge3J9Cihqb2Jyb2xlX2F0dHJpdGlvbl9wbG90IDwtIGdncGxvdChqb2Jyb2xlX2F0dHJpdGlvbiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGFlcyhyZW9yZGVyKEpvYlJvbGUsIEF0dHJpdGlvblJhdGUpLCBBdHRyaXRpb25SYXRlKSkgKwogIGdlb21fY29sKGFlcyhmaWxsID0gJycpKSArCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gYygiIzFEMjQzQyIpLGd1aWRlPUZBTFNFKSArCiAgdGhlbWVfbWluaW1hbCgpICsKICBzY2FsZV95X2NvbnRpbnVvdXMobGFiZWxzPXBlcmNlbnQpICsKICBsYWJzKGxpc3QoeSA9IHBhc3RlKCJBdHRyaXRpb24gaW4gIixtb250aCxzZXAgPSAiIiksIHggPSAiSm9iIFJvbGUiLAogICAgICAgICAgICB0aXRsZSA9IHBhc3RlKCJBdHRyaXRpb24gYnkgSm9iIFJvbGUsICIsIGRhdGUsIHNlcCA9ICIiKSkpICsKICB0aGVtZShwYW5lbC5ncmlkLm1pbm9yID0gZWxlbWVudF9ibGFuaygpKSArCiAgY29vcmRfZmxpcCgpKQpgYGAKSSB3YW50IG15IHRhYmxlIHRvIGJlIHNvcnRlZCBmcm9tIGhpZ2hlc3QgdG8gbG93ZXN0LCBzbyBmaXJzdCBJIHNvcnQgbXkgZGF0YXNldC4KYGBge3J9CmpvYnJvbGVfYXR0cml0aW9uIDwtIGpvYnJvbGVfYXR0cml0aW9uW29yZGVyKGpvYnJvbGVfYXR0cml0aW9uJEF0dHJpdGlvblJhdGUsIGRlY3JlYXNpbmcgPSBUUlVFKSxdCmBgYAoKQWx0aG91Z2ggSSB3b24ndCBpbmNsdWRlIGl0IGluIHRoaXMgdGFibGUsIEkgY2FuIHVzZSB0aGlzIGluZm9ybWF0aW9uIHRvIGdldCB0aGUgaW1wYWN0IG9mIHJlZHVjaW5nIGF0dHJpdGlvbiBmb3IgdGhlIGpvYiByb2xlIHdpdGggdGhlIGhpZ2hlc3QgYXR0cml0aW9uLiBJJ2xsIHVzZSB0aGlzIGluIHRoZSByZWNvbW1lbmRhdGlvbnMgc2VjdGlvbiwgYW5kIGl0J3MgZWFzaWVyIHRvIGNhbGN1bGF0ZSBpdCBub3cuCgpgYGB7cn0KcmVkdWN0aW9uID0gLjA1ICAgICAgICAgICAgIyBob3cgbXVjaCBjb3VsZCB3ZSByZWR1Y2UgYXR0cml0aW9uPwpyZXBsYWNlbWVudF9tdWx0ID0gMS41ICAgICAgICMgaG93IG11Y2ggZG9lcyBpdCBjb3N0IHRvIHJlcGxhY2UgYW4gZW1wbG95ZWUsIGFzIGEgbXVsdGlwbGllciBvZiB0aGVpciBzYWxhcnk/CnRvcF9yb2xlIDwtIGFzLmNoYXJhY3Rlcihqb2Jyb2xlX2F0dHJpdGlvbiRKb2JSb2xlWzFdKSAgICMgam9iIHJvbGUgd2l0aCBoaWdoZXN0IGF0dHJpdGlvbgp0b3Bfcm9sZV9hdHRyaXRpb24gPC0gam9icm9sZV9hdHRyaXRpb24kQXR0cml0aW9uUmF0ZVsxXSAjIGF0dHJpdGlvbiBmb3IgdGhhdCByb2xlCmxlZnRfZnJvbV90b3Bfcm9sZSA8LSBteWRhdGEkSm9iUm9sZSA9PSB0b3Bfcm9sZSAmIG15ZGF0YSRBdHRyaXRpb24gPT0gMSAKc2FsYXJ5X2xvc3QgPSAKICBzdW0obXlkYXRhJE1vbnRobHlJbmNvbWVbbGVmdF9mcm9tX3RvcF9yb2xlXSkgKiAxMiAqICAgICAjIHRvdGFsIGFubnVhbCBzYWxhcnkgbG9zdCAKICByZXBsYWNlbWVudF9tdWx0ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjIHRpbWVzIHJlcGxhY2VtZW50IGNvc3QKaW1wYWN0ID0gc2FsYXJ5X2xvc3QgKiByZWR1Y3Rpb24gIyBhbW91bnQgd2UgY291bGQgc2F2ZQpgYGAKCkZvcm1hdHRpbmcgdGhlIHRhYmxlIHNob3VsZCBsb29rIGZhbWlsaWFyLgpgYGB7cn0gCgojIGZvcm1hdCBhcyBwZXJjZW50YWdlcywgcmVuYW1lIGNvbHVtbnMKam9icm9sZV9hdHRyaXRpb24kQXR0cml0aW9uUmF0ZSA8LSBwZXJjZW50KGpvYnJvbGVfYXR0cml0aW9uJEF0dHJpdGlvblJhdGUpCm5hbWVzKGpvYnJvbGVfYXR0cml0aW9uKSA9IGMoJ0pvYiBSb2xlJywgJ0hlYWRjb3VudCcsICdBdHRyaXRpb24nLCAnQXR0cml0aW9uICUnKQoKIyBwdXQgZGF0YSBpbnRvIGEgdGFibGUKb3B0aW9ucygiUmVwb3J0ZVJzLWZvbnRzaXplIj0xNikgI2ZvbnQgc2l6ZSBmb3IgdGFibGVzCnRhYmxlX3dpZHRocyA9IGMoMi44LCAxLjIsIDEsIDEuNikgCgpqb2Jyb2xlX2F0dHJpdGlvbl90YWJsZSA8LSB2YW5pbGxhLnRhYmxlKGpvYnJvbGVfYXR0cml0aW9uKSAlPiUKICBzZXRaZWJyYVN0eWxlKG9kZCA9ICcjZWVlZWVlJywgZXZlbiA9ICd3aGl0ZScgKSAlPiUKICBzZXRGbGV4VGFibGVXaWR0aHMod2lkdGhzID0gdGFibGVfd2lkdGhzKSAKam9icm9sZV9hdHRyaXRpb25fdGFibGVbLDI6NF0gPSBwYXJDZW50ZXIoKQpqb2Jyb2xlX2F0dHJpdGlvbl90YWJsZVssMV0gPSBwYXJMZWZ0KCkKam9icm9sZV9hdHRyaXRpb25fdGFibGVbLCx0bz0naGVhZGVyJ10gPSBwYXJDZW50ZXIoKQpqb2Jyb2xlX2F0dHJpdGlvbl90YWJsZQpgYGAKTm93IEknbSBnb2luZyB0byBtYWtlIHRoZSBzYW1lIGdyYXBoIGFuZCB0YWJsZSBmb3IgcGVyZm9ybWFuY2UgYW5kIGhpcmUgc291cmNlLiBUaGUgb25seSBkaWZmZXJlbmNlIGlzIEkgdXNlIGBzY2FsZV94X3JldmVyc2VgIG9uIHBlcmZvcm1hbmNlIHJhdGluZyBzbyB0aGF0IHRoZSAnMScgcmF0aW5nIGlzIGF0IHRoZSB0b3Agb2YgdGhlIGdyYXBoLiBJJ20gYWxzbyBjYWxjdWxhdGluZyB0aGUgaGlyZSBzb3VyY2VzIHRoYXQgaGF2ZSB0aGUgaGlnaGVzdCBhbmQgbG93ZXN0IGF0dHJpdGlvbiByYXRlcywgZm9yIHVzZSBpbiB0aGUgcmVjb21tZW5kYXRpb25zIHNlY3Rpb24uCgojIyMjIEF0dHJpdGlvbiBieSBQZXJmb3JtYW5jZQpgYGB7cn0KcGVyZm9ybWFuY2VfYXR0cml0aW9uIDwtIGFzLmRhdGEuZnJhbWUoCiAgYXMubGlzdChhZ2dyZWdhdGUoQXR0cml0aW9uIH4gUGVyZm9ybWFuY2VSYXRpbmcsCiAgICAgICAgICAgICAgICAgICAgZGF0YSA9IG15ZGF0YSwgRlVOID0gZnVuY3Rpb24oeCkgYygKICAgICAgICAgICAgICAgICAgICAgIG4gPSBsZW5ndGgoeCksCiAgICAgICAgICAgICAgICAgICAgICBzID0gc3VtKHgsIG5hLnJtID0gVCksCiAgICAgICAgICAgICAgICAgICAgICBtbiA9IG1lYW4oeCwgbmEucm0gPSBUKSkKICAgICAgICAgICAgICAgICAgICApCiAgICAgICAgICApKQoKbmFtZXMocGVyZm9ybWFuY2VfYXR0cml0aW9uKSA9IGMoJ1BlcmZvcm1hbmNlUmF0aW5nJywgJ0hlYWRjb3VudCcsICdBdHRyaXRpb24nLCAnQXR0cml0aW9uUmF0ZScpCgoocGVyZm9ybWFuY2VfYXR0cml0aW9uX3Bsb3QgPC0gZ2dwbG90KHBlcmZvcm1hbmNlX2F0dHJpdGlvbiwgYWVzKFBlcmZvcm1hbmNlUmF0aW5nLCBBdHRyaXRpb25SYXRlKSkgKwogIGdlb21fYmFyKHN0YXQgPSAiaWRlbnRpdHkiLCBhZXMoZmlsbCA9ICcnKSkgKwogIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IGMoIiMxRDI0M0MiKSxndWlkZT1GQUxTRSkgKwogIHRoZW1lX21pbmltYWwoKSArCiAgc2NhbGVfeV9jb250aW51b3VzKGxhYmVscz1wZXJjZW50KSArCiAgc2NhbGVfeF9yZXZlcnNlKCkgKwogIGxhYnMobGlzdCh5ID0gcGFzdGUoIkF0dHJpdGlvbiBpbiAiLG1vbnRoLHNlcCA9ICIiKSwgeCA9ICJQZXJmb3JtYW5jZSBSYXRpbmciLAogICAgICAgICAgICB0aXRsZSA9IHBhc3RlKCJBdHRyaXRpb24gYnkgUGVyZm9ybWFuY2UgUmF0aW5nLCAiLCBkYXRlLCBzZXAgPSAiIikpKSArCiAgdGhlbWUocGFuZWwuZ3JpZC5taW5vciA9IGVsZW1lbnRfYmxhbmsoKSkgKwogIGNvb3JkX2ZsaXAoKSkKCiMgZm9ybWF0IGFzIHBlcmNlbnRhZ2VzLCByZW5hbWUgY29sdW1ucwpwZXJmb3JtYW5jZV9hdHRyaXRpb24kQXR0cml0aW9uUmF0ZSA8LSBwZXJjZW50KHBlcmZvcm1hbmNlX2F0dHJpdGlvbiRBdHRyaXRpb25SYXRlKQpuYW1lcyhwZXJmb3JtYW5jZV9hdHRyaXRpb24pID0gYygnUGVyZm9ybWFuY2UgUmF0aW5nJywgJ0hlYWRjb3VudCcsICdBdHRyaXRpb24nLCAnQXR0cml0aW9uICUnKQoKIyBwdXQgZGF0YSBpbnRvIGEgdGFibGUKcGVyZm9ybWFuY2VfYXR0cml0aW9uX3RhYmxlIDwtIHZhbmlsbGEudGFibGUocGVyZm9ybWFuY2VfYXR0cml0aW9uKSAlPiUKICBzZXRaZWJyYVN0eWxlKG9kZCA9ICcjZWVlZWVlJywgZXZlbiA9ICd3aGl0ZScgKSAlPiUKICBzZXRGbGV4VGFibGVXaWR0aHMod2lkdGhzID0gdGFibGVfd2lkdGhzKSAKcGVyZm9ybWFuY2VfYXR0cml0aW9uX3RhYmxlWywxOjRdID0gcGFyQ2VudGVyKCkKcGVyZm9ybWFuY2VfYXR0cml0aW9uX3RhYmxlWywsdG89J2hlYWRlciddID0gcGFyQ2VudGVyKCkKcGVyZm9ybWFuY2VfYXR0cml0aW9uX3RhYmxlCmBgYAoKIyMjIyBBdHRyaXRpb24gYnkgUmVjcnVpdGluZyBDaGFubmVsCmBgYHtyfQpoaXJlc291cmNlX2F0dHJpdGlvbiA8LSBhcy5kYXRhLmZyYW1lKAogIGFzLmxpc3QoYWdncmVnYXRlKEF0dHJpdGlvbiB+IEhpcmVTb3VyY2UsIGRhdGEgPSBteWRhdGEsCiAgICAgICAgICAgICAgICAgICAgRlVOID0gZnVuY3Rpb24oeCkgYygKICAgICAgICAgICAgICAgICAgICAgIG4gPSBsZW5ndGgoeCksCiAgICAgICAgICAgICAgICAgICAgICBzID0gc3VtKHgsIG5hLnJtID0gVCksCiAgICAgICAgICAgICAgICAgICAgICBtbiA9IG1lYW4oeCwgbmEucm0gPSBUKSkKICAgICAgICAgICAgICAgICAgICApCiAgICAgICAgICApKQoKbmFtZXMoaGlyZXNvdXJjZV9hdHRyaXRpb24pID0gYygnSGlyZVNvdXJjZScsICdIZWFkY291bnQnLCAnQXR0cml0aW9uJywgJ0F0dHJpdGlvblJhdGUnKQoKKGhpcmVzb3VyY2VfYXR0cml0aW9uX3Bsb3QgPC0gZ2dwbG90KGhpcmVzb3VyY2VfYXR0cml0aW9uLCBhZXMocmVvcmRlcihIaXJlU291cmNlLCBBdHRyaXRpb25SYXRlKSwgQXR0cml0aW9uUmF0ZSkpICsKICBnZW9tX2JhcihzdGF0ID0gImlkZW50aXR5IiwgYWVzKGZpbGwgPSAnJykpICsKICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBjKCIjMUQyNDNDIiksZ3VpZGU9RkFMU0UpICsKICB0aGVtZV9taW5pbWFsKCkgKwogIHNjYWxlX3lfY29udGludW91cyhsYWJlbHM9cGVyY2VudCkgKwogIGxhYnMobGlzdCh5ID0gcGFzdGUoIkF0dHJpdGlvbiBpbiAiLG1vbnRoLHNlcCA9ICIiKSwgeCA9ICJKb2IgTGV2ZWwiLAogICAgICAgICAgICB0aXRsZSA9IHBhc3RlKCJBdHRyaXRpb24gYnkgUmVjcnVpdGluZyBDaGFubmVsLCAiLCBkYXRlLCBzZXAgPSAiIikpKSArCiAgdGhlbWUocGFuZWwuZ3JpZC5taW5vciA9IGVsZW1lbnRfYmxhbmsoKSkgKwogIGNvb3JkX2ZsaXAoKSkKCiMgc29ydCBieSBhdHRyaXRpb24gcmF0ZSwgZm9ybWF0IGFzIHBlcmNlbnRhZ2VzLCByZW5hbWUgY29sdW1ucwpoaXJlc291cmNlX2F0dHJpdGlvbiA8LSBoaXJlc291cmNlX2F0dHJpdGlvbltvcmRlcihoaXJlc291cmNlX2F0dHJpdGlvbiRBdHRyaXRpb25SYXRlLCBkZWNyZWFzaW5nID0gVFJVRSksXQpoaXJlc291cmNlX2F0dHJpdGlvbiRBdHRyaXRpb25SYXRlIDwtIHBlcmNlbnQoaGlyZXNvdXJjZV9hdHRyaXRpb24kQXR0cml0aW9uUmF0ZSkKbmFtZXMoaGlyZXNvdXJjZV9hdHRyaXRpb24pID0gYygnUmVjcnVpdGluZyBDaGFubmVsJywgJ0hlYWRjb3VudCcsICdBdHRyaXRpb24nLCAnQXR0cml0aW9uICUnKQoKIyBwdXQgZGF0YSBpbnRvIGEgdGFibGUgCmhpcmVzb3VyY2VfYXR0cml0aW9uX3RhYmxlIDwtIHZhbmlsbGEudGFibGUoaGlyZXNvdXJjZV9hdHRyaXRpb24pICU+JQogIHNldFplYnJhU3R5bGUob2RkID0gJyNlZWVlZWUnLCBldmVuID0gJ3doaXRlJyApICU+JQogIHNldEZsZXhUYWJsZVdpZHRocyh3aWR0aHMgPSB0YWJsZV93aWR0aHMpIApoaXJlc291cmNlX2F0dHJpdGlvbl90YWJsZVssMV0gPSBwYXJMZWZ0KCkKaGlyZXNvdXJjZV9hdHRyaXRpb25fdGFibGVbLDI6NF0gPSBwYXJDZW50ZXIoKQpoaXJlc291cmNlX2F0dHJpdGlvbl90YWJsZVssLHRvPSdoZWFkZXInXSA9IHBhckNlbnRlcigpCmhpcmVzb3VyY2VfYXR0cml0aW9uX3RhYmxlCgojIHNhdmUgaGlyZSBzb3VyY2VzIHdpdGggaGlnaGVzdCBhbmQgbG93ZXN0IGxldmVscyBvZiBhdHRyaXRpb24KdG9wX2hpcmUgPC0gaGlyZXNvdXJjZV9hdHRyaXRpb24kYFJlY3J1aXRpbmcgQ2hhbm5lbGBbMV0KYm90dG9tX2hpcmUgPC0gaGlyZXNvdXJjZV9hdHRyaXRpb24kYFJlY3J1aXRpbmcgQ2hhbm5lbGBbbnJvdyhoaXJlc291cmNlX2F0dHJpdGlvbildCmBgYApXaGF0J3MgZ3JlYXQgYWJvdXQgdXNpbmcgUiB0byBkbyB0aGUgZ3JhcGhzIGlzIHRoYXQgd2hlbmV2ZXIgSSBuZWVkIHRvIHVwZGF0ZSB0aGlzIHJlcG9ydCB3aXRoIG5ldyBkYXRhLCBhbGwgSSBoYXZlIHRvIGRvIGlzIGNoYW5nZSB0aGUgZGF0ZSwgYW5kIHRoZSByZXN0IGhhcHBlbnMgYXV0b21hdGljYWxseS4gSXQgdGFrZXMgYSBsaXR0bGUgbW9yZSBpbnZlc3RtZW50IGF0IGZpcnN0LCBidXQgbm93IGFsbCB0aGF0IGRyZWFkZWQgRXhjZWwgd29yayB3aWxsIHRha2UgYWxtb3N0IG5vIHRpbWUgYXQgYWxsISAKCiMjIENyZWF0aW5nIHRoZSBSZXBvcnQKIyMjIyBFeHBsYW5hdGlvbgpPbmNlIHRoZSBjaGFydHMgYW5kIHRhYmxlcyBhcmUgZmluaXNoZWQsIEkgbmVlZCB0byBwdXQgdGhlbSBpbnRvIFBvd2VyUG9pbnQuIEkndmUgZG9uZSB0aGlzIG1hbnkgdGltZXMgYnkgY29weS1wYXN0aW5nIHRoZSBncmFwaHMgZnJvbSBSIGludG8gdGhlIHJlcG9ydCwgYnV0IGl0J3MgdGVkaW91cyBhbmQgaGFyZCB0byBiZSBjb25zaXN0ZW50IHdpdGggdGhlaXIgc2l6ZSBhbmQgcGxhY2VtZW50LgoKQWdhaW4sIGZvciBtb3JlIGRldGFpbHMgYWJvdXQgaG93IHRoaXMgd29ya3MsIHRoZSBndWlkZSBpcyBvbiB0aGUgYXV0aG9yJ3MgZ2l0aHViIHBhZ2UgKGh0dHA6Ly9kYXZpZGdvaGVsLmdpdGh1Yi5pby9SZXBvcnRlUnMvYXJ0aWNsZXMvcG93ZXJwb2ludC5odG1sKS4gCgpGaXJzdCBJIGNyZWF0ZSBhIG5ldyBgcHB0eGAgZG9jdW1lbnQgYW5kIGNhbGwgaXQgYHJlcG9ydGAuIEkgc2V0IHRoZSBmb250IHNpemUgdG8gMjQsIGFuZCBJIGxpa2UgdG8gdXNlIGBzbGlkZS5sYXlvdXRzKHJlcG9ydClgIHRvIHNlZSB3aGljaCBQb3dlclBvaW50IHNsaWRlIGxheW91dHMgYXJlIGF2YWlsYWJsZS4gVGhlIGJhc2ljIGlkZWEgaXMgdG8gYWRkIGEgc2xpZGUgKGBhZGRTbGlkZWApLCBjaG9vc2UgYSBsYXlvdXQsIGFuZCB0aGVuIGZpbGwgdGhhdCBsYXlvdXQgd2l0aCBhIHRpdGxlIChgYWRkVGl0bGVgKSBhbmQgdGV4dCAoYGFkZFBhcmFncmFwaGApLCBhIHRhYmxlIChgYWRkRmxleFRhYmxlYCksIG9yIGEgZ3JhcGggKGBhZGRQbG90YCkuIEZpbmFsbHksIEkgdXNlIGB3cml0ZURvY2AgdG8gc2F2ZSB0aGUgZmlsZS4gSSdtIHNhdmluZyBpdCBpbnRvIHRoZSBocl9kYXRhIGZvbGRlci4gWW91J2xsIG5lZWQgdG8gbWFrZSBzdXJlIHRoYXQgcGF0aCBtYXRjaGVzIHdoZXJlIHlvdSB3YW50IHRvIHNhdmUgaXQgdG8uCgpZb3UnbGwgYWxzbyBzZWUgYW4gYXJndW1lbnQgZm9yIHRlbXBsYXRlIC0gSSdtIHVzaW5nIGEgUG93ZXJQb2ludCB0ZW1wbGF0ZSBmb3IgdGhpcyByZXBvcnQuIFRoaXMgbWVhbnMgeW91IGNhbiBidWlsZCB0aGVzZSByZXBvcnRzIGRpcmVjdGx5IG9uIHlvdXIgY29ycG9yYXRlIHJlcG9ydCB0ZW1wbGF0ZS4gSSBoYXZlIHRoZSB0ZW1wbGF0ZSBzYXZlZCBpbiB0aGUgaHJfZGF0YSBmb2xkZXIgb24gbXkgbWFjaGluZS4gWW91J2xsIG5lZWQgdG8gdXBkYXRlIHRoYXQgcGF0aCB3aXRoIHdoZXJldmVyIHlvdXIgdGVtcGxhdGUgaXMgc3RvcmVkLgoKV2hlbiB5b3Ugc2VlIGNvZGUgbGlrZSB0aGlzOiBgcGFyLnByb3BlcnRpZXMgPSBwYXJQcm9wZXJ0aWVzKGxpc3Quc3R5bGUgPSAidW5vcmRlcmVkIiwgbGV2ZWwgPSAxKWAsIFRoYXQncyBob3cgUmVwb3J0ZVJzIGNhbiBwcm9kdWNlIGluZGVudGVkIGJ1bGxldCBwb2ludCBsaXN0cy4gTGV2ZWwgPSAyIHdvdWxkIGluZGVudCB0aGUgYnVsbGV0cyBvbmUgbGV2ZWwsIGxldmVsID0gMyB3b3VsZCBiZSBhbm90aGVyIGxldmVsLgoKSWYgeW91J3ZlIG5ldmVyIHNlZW4gdGhlICU+JSBvcGVyYXRvciwgeW91IGNhbiByZWFkIGl0IGxpa2UsICJ0aGVuIi4gU28gdGhlIGZvbGxvd2luZyBjb2RlOgpgYGB7ciwgZXZhbCA9IEZBTFNFfQpyZXBvcnQgPC0gcmVwb3J0ICU+JQogIGFkZFNsaWRlKHNsaWRlLmxheW91dCA9ICJUaXRsZSBTbGlkZSIpICU+JQogIGFkZFRpdGxlKHBhc3RlKGRhdGUsICJBY21lIENvLiBBdHRyaXRpb24gUmVwb3J0IikpCmBgYApjYW4gYmUgcmVhZCwgInRha2UgYHJlcG9ydGAsIHRoZW4gYWRkIGEgdGl0bGUgc2xpZGUsIHRoZW4gYWRkIGEgdGl0bGUsIGFuZCBzYXZlIGl0IGFzIGByZXBvcnRgLiIKCiMjIyMgQ29kZSB0byBwcm9kdWNlIGFuZCBwdWJsaXNoIHRoZSByZXBvcnQKYGBge3IgcHB0X3ByaW50fQpyZXBvcnQgPC0gcHB0eCh0aXRsZSA9IHBhc3RlKGRhdGUsICJBY21lIENvLiBcbkF0dHJpdGlvbiBSZXBvcnQiKSwKICAgICAgICAgICAgICB0ZW1wbGF0ZSA9ICd+L2hyX2RhdGEvZXhhbXBsZV9jb3Jwb3JhdGVfdGVtcGxhdGUucHB0eCcpCm9wdGlvbnMoIlJlcG9ydGVScy1mb250c2l6ZSI9MjQpICNmb250IHNpemUgZm9yIHRleHQKc2xpZGUubGF5b3V0cyhyZXBvcnQpCgpyZXBvcnQgPC0gcmVwb3J0ICU+JQogIGFkZFNsaWRlKHNsaWRlLmxheW91dCA9ICJUaXRsZSBTbGlkZSIpICU+JQogIGFkZFRpdGxlKHBhc3RlKGRhdGUsICJBY21lIENvLiBBdHRyaXRpb24gUmVwb3J0IikpCgoKCnJlcG9ydCA8LSByZXBvcnQgJT4lCiAgYWRkU2xpZGUoc2xpZGUubGF5b3V0ID0gIlRpdGxlIGFuZCBDb250ZW50IikgJT4lCiAgYWRkVGl0bGUoIlRhYmxlIG9mIENvbnRlbnRzIikgJT4lCiAgYWRkUGFyYWdyYXBoKCBjKCJPdmVyYWxsIEF0dHJpdGlvbiBTdGF0aXN0aWNzIiksIAogICAgICAgICAgICAgICAgcGFyLnByb3BlcnRpZXMgPSBwYXJQcm9wZXJ0aWVzKGxpc3Quc3R5bGUgPSAidW5vcmRlcmVkIiwgbGV2ZWwgPSAxKSkgJT4lCiAgYWRkUGFyYWdyYXBoKCBjKCJBdHRyaXRpb24gYnkgU3ViZ3JvdXBzIiksIGFwcGVuZCA9IFQsCiAgICAgICAgICAgICAgICBwYXIucHJvcGVydGllcyA9IHBhclByb3BlcnRpZXMobGlzdC5zdHlsZSA9ICJ1bm9yZGVyZWQiLCBsZXZlbCA9IDEpKSAlPiUKICBhZGRQYXJhZ3JhcGgoIHNldF9vZl9wYXJhZ3JhcGhzKCJieSBKb2IgUm9sZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiYnkgUGVyZm9ybWFuY2UgUmF0aW5nIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJieSBKb2IgTGV2ZWwiKSwKICAgICAgICAgICAgICAgIGFwcGVuZCA9IFQsCiAgICAgICAgICAgICAgICBwYXIucHJvcGVydGllcyA9IHBhclByb3BlcnRpZXMobGlzdC5zdHlsZSA9ICJ1bm9yZGVyZWQiLCBsZXZlbCA9IDIpICklPiUKICBhZGRQYXJhZ3JhcGgoIGMoIlJlY29tbWVuZGF0aW9ucyIpLCBhcHBlbmQgPSBULAogICAgICAgICAgICAgICAgcGFyLnByb3BlcnRpZXMgPSBwYXJQcm9wZXJ0aWVzKGxpc3Quc3R5bGUgPSAidW5vcmRlcmVkIiwgbGV2ZWwgPSAxKSkKCnJlcG9ydCA8LSByZXBvcnQgJT4lCiAgYWRkU2xpZGUoc2xpZGUubGF5b3V0ID0gIlNlY3Rpb24gSGVhZGVyIikgJT4lCiAgYWRkVGl0bGUoIk92ZXJhbGwgQXR0cml0aW9uIFN0YXRpc3RpY3MiKQoKcmVwb3J0IDwtIHJlcG9ydCAlPiUKICBhZGRTbGlkZShzbGlkZS5sYXlvdXQgPSAiVGl0bGUgYW5kIENvbnRlbnQiKSAlPiUKICBhZGRUaXRsZShwYXN0ZTAoIlN1bW1hcnkgb2YgQXR0cml0aW9uIGluICIsIGRhdGUpKSAlPiUKICBhZGRGbGV4VGFibGUob3ZlcmFsbF9hdHRyaXRpb25fdGFibGUpCgoKcmVwb3J0IDwtIHJlcG9ydCAlPiUKICBhZGRTbGlkZShzbGlkZS5sYXlvdXQgPSAiU2VjdGlvbiBIZWFkZXIiKSAlPiUKICBhZGRUaXRsZSgiQXR0cml0aW9uIGJ5IFN1Ymdyb3VwIikKCnJlcG9ydCA8LSByZXBvcnQgJT4lCiAgYWRkU2xpZGUoc2xpZGUubGF5b3V0ID0gIlR3byBDb250ZW50IikgJT4lCiAgYWRkVGl0bGUoIldoaWNoIEpvYnMgaGF2ZSB0aGUgSGlnaGVzdCBBdHRyaXRpb24/IikgJT4lCiAgYWRkUGxvdChmdW5jdGlvbigpIHByaW50KGpvYnJvbGVfYXR0cml0aW9uX3Bsb3QpKSAlPiUKICBhZGRGbGV4VGFibGUoam9icm9sZV9hdHRyaXRpb25fdGFibGUpCgpyZXBvcnQgPC0gcmVwb3J0ICU+JQogIGFkZFNsaWRlKHNsaWRlLmxheW91dCA9ICJUd28gQ29udGVudCIpICU+JQogIGFkZFRpdGxlKCJBcmUgV2UgS2VlcGluZyBPdXIgVG9wIFBlcmZvcm1lcnM/IikgJT4lCiAgYWRkUGxvdChmdW5jdGlvbigpIHByaW50KHBlcmZvcm1hbmNlX2F0dHJpdGlvbl9wbG90KSkgJT4lCiAgYWRkRmxleFRhYmxlKHBlcmZvcm1hbmNlX2F0dHJpdGlvbl90YWJsZSkgCgpyZXBvcnQgPC0gcmVwb3J0ICU+JQogIGFkZFNsaWRlKHNsaWRlLmxheW91dCA9ICJUd28gQ29udGVudCIpICU+JQogIGFkZFRpdGxlKCJXaGljaCBIaXJpbmcgQ2hhbm5lbHMgaGF2ZSBIaWdoIFR1cm5vdmVyPyIpICU+JQogIGFkZFBsb3QoZnVuY3Rpb24oKSBwcmludChoaXJlc291cmNlX2F0dHJpdGlvbl9wbG90KSkgJT4lCiAgYWRkRmxleFRhYmxlKGhpcmVzb3VyY2VfYXR0cml0aW9uX3RhYmxlKQoKcmVwb3J0IDwtIHJlcG9ydCAlPiUKICBhZGRTbGlkZShzbGlkZS5sYXlvdXQgPSAiVGl0bGUgYW5kIENvbnRlbnQiKSAlPiUKICBhZGRUaXRsZSgiS2V5IEluc2lnaHRzIikgJT4lCiAgYWRkUGFyYWdyYXBoKCBjKHBhc3RlMCgiVGhlIGpvYiBncm91cCB3aXRoIHRoZSBoaWdoZXN0IGF0dHJpdGlvbiB3YXMgIiwgdG9wX3JvbGUsCiAgICAgICAgICAgICAgICAgICAgICAgICIgd2l0aCBhbiBhdHRyaXRpb24gcmF0ZSBvZiAiLGFzLmNoYXJhY3Rlcihqb2Jyb2xlX2F0dHJpdGlvbiRgQXR0cml0aW9uICVgWzFdKSwiLiIpKSwgCiAgICAgICAgICAgICAgICBwYXIucHJvcGVydGllcyA9IHBhclByb3BlcnRpZXMobGlzdC5zdHlsZSA9ICJ1bm9yZGVyZWQiLCBsZXZlbCA9IDEpKSAlPiUKICBhZGRQYXJhZ3JhcGgoIGMocGFzdGUwKCJBZGRpdGlvbmFsIGZvY3VzIHNob3VsZCBiZSBwbGFjZWQgb24gcmV0YWluaW5nICIsIHRvcF9yb2xlLAogICAgICAgICAgICAgICAgICAgICAgICAicy4iKSksCiAgICAgICAgICAgICAgICBhcHBlbmQgPSBULAogICAgICAgICAgICAgICAgcGFyLnByb3BlcnRpZXMgPSBwYXJQcm9wZXJ0aWVzKGxpc3Quc3R5bGUgPSAidW5vcmRlcmVkIiwgbGV2ZWwgPSAyKSkgJT4lCiAgYWRkUGFyYWdyYXBoKCBjKHBhc3RlMCgiV2UgZXN0aW1hdGUgdGhhdCByZXBsYWNpbmcgYW4gZW1wbG95ZWUgY29zdHMgIiwgcmVwbGFjZW1lbnRfbXVsdCwKICAgICAgICAgICAgICAgICAgICAgICAgICJ4IHRoZWlyIGFubnVhbCBzYWxhcnkuIFJlZHVjaW5nIGF0dHJpdGlvbiBieSAiLCBwZXJjZW50KHJlZHVjdGlvbiksCiAgICAgICAgICAgICAgICAgICAgICAgICAiIGluICIsIG1vbnRoLCAiIGNvdWxkIGhhdmUgc2F2ZWQgIiwgZG9sbGFyKHJvdW5kKGltcGFjdCwwKSksICIuIikpLAogICAgICAgICAgICAgICAgYXBwZW5kID0gVCwKICAgICAgICAgICAgICAgIHBhci5wcm9wZXJ0aWVzID0gcGFyUHJvcGVydGllcyhsaXN0LnN0eWxlID0gInVub3JkZXJlZCIsIGxldmVsID0gMikpICU+JQogIGFkZFBhcmFncmFwaCggYyhwYXN0ZTAoIk91ciAiLCBoaXJlc291cmNlX2F0dHJpdGlvbiRgUmVjcnVpdGluZyBDaGFubmVsYFsxXSwKICAgICAgICAgICAgICAgICAgICAgICAgICIgaGlyZXMgaGF2ZSB0aGUgaGlnaGVzdCBhdHRyaXRpb24gcmF0ZS4gVGhlIGNoYW5uZWwgd2l0aCB0aGUgbG93ZXN0IGF0dHJpdGlvbiByYXRlIGluICIsCiAgICAgICAgICAgICAgICAgICAgICAgICBtb250aCwgIiB3YXMgdGhlICIsCiAgICAgICAgICAgICAgICAgICAgICAgICBoaXJlc291cmNlX2F0dHJpdGlvbiRgUmVjcnVpdGluZyBDaGFubmVsYFtucm93KGhpcmVzb3VyY2VfYXR0cml0aW9uKV0sICIgY2hhbm5lbC4iKSksIAogICAgICAgICAgICAgICAgYXBwZW5kID0gVCwKICAgICAgICAgICAgICAgIHBhci5wcm9wZXJ0aWVzID0gcGFyUHJvcGVydGllcyhsaXN0LnN0eWxlID0gInVub3JkZXJlZCIsIGxldmVsID0gMSkpIAoKd3JpdGVEb2MocmVwb3J0LCBwYXN0ZTAoIn4vaHJfZGF0YS9leGFtcGxlcy8iLGRhdGUsIiBBdHRyaXRpb24gUmVwb3J0LnBwdHgiKSkKYGBgCgoKQW5kIHRoZXJlIHlvdSBoYXZlIGl0ISBUaGUgZmluYWwgcmVwb3J0IGNhbiBiZSBzZWVuIG9uIFNsaWRlU2hhcmUgKGh0dHA6Ly93d3cuc2xpZGVzaGFyZS5uZXQvQmVuVGV1c2NoL2F1dG9tYXRlZC1hdHRyaXRpb24tcmVwb3J0LXVzaW5nLXIpLgpBZ2FpbiwgaWYgeW91J3JlIGludGVyZXN0ZWQgaW4gc3RhcnRpbmcgd2l0aCBhIHNpbXBsZSB2ZXJzaW9uIG9mIHRoaXMgY29kZSwgY2hlY2sgb3V0IGh0dHA6Ly9ycHVicy5jb20vdGV1c2NoYi9tYWtpbmdfcHB0X3NsaWRlc19zaW1wbGUuCgpJIGhvcGUgdGhpcyBndWlkZSBjYW4gYmUgaGVscGZ1bCBmb3IgYW5hbHl0aWNzIHRlYW1zLCBhbmQgZXNwZWNpYWxseSBwZW9wbGUgYW5hbHl0aWNzIHRlYW1zIG9yIEhSIHJlcG9ydGluZyB0ZWFtcywgd2hvIHdvdWxkIGxpa2UgdG8gcmVkdWNlIHRoZSBhbW91bnQgb2YgdGltZSB0aGV5IHNwZW5kIG9uIHJvdXRpbmUgcmVwb3J0cy4gV2VsbC1jb25zaWRlcmVkIHJlcG9ydGluZyBpcyBzdGlsbCBhIHBpbGxhciBvZiBlZmZlY3RpdmUgcGVvcGxlIGFuYWx5dGljcywgYnV0IHRoZSBiZXN0IHRlYW1zIHJlZHVjZSB0aGUgdGltZSB0aGV5IHNwZW5kIG9uIGl0IGFzIG11Y2ggYXMgcG9zc2libGUsIHNvIHRoZXkgY2FuIHdvcmsgb24gbW9yZSBhZHZhbmNlZCBhbmFseXRpY3MuIElmIHlvdSBmaW5kIG90aGVyIGhlbHBmdWwgZ3VpZGVzIGZvciBwZW9wbGUgYW5hbHl0aWNzLCBvciB3b3VsZCBsaWtlIHRvIHNlZSBzb21ldGhpbmcgc3BlY2lmaWMsIHBsZWFzZSBsZXQgbWUga25vdyBpbiB0aGUgY29tbWVudHMh