Module 2: Handling Data in R
Now that we have a basic understanding of R’s syntax and the RStudio
application, we can start working with data!
This module will cover:
- Working with built-in datasets in R
- Understanding and handling missing data (
NA
values)
- Creating synthetic datasets for testing and examples
- Different ways to view and explore data in RStudio
- Extracting specific data from vectors and data frames
- Loading and saving data locally and online
- Common pitfalls and gotchas when working with R
By the end of this module, you’ll be comfortable loading data,
inspecting it for problems, and extracting the parts you need for
analysis. We’ll use both base R and tidyverse approaches, helping you
understand both the traditional and modern ways of working with data in
R.
Built-in Datasets
Many R packages include example datasets. Let’s look at two:
# View the first few rows of the starwars data set
library(dplyr)
head(starwars)
# Another dataset that includes the fuel efficiency of 32 cars.
mean(mtcars$mpg)
[1] 20.09062
These datasets are frequently used in help files, online discussions,
or blog posts demonstrating R functions.
Notice the NA
hair color in the table? That’s how R
represents missing data.
Missing Data in R
R uses NA
(Not Available) to represent missing values.
These are different from zeros or empty strings. When you perform
calculations with NA
values, the result will be
NA
unless you explicitly tell R how to handle the missing
data. This is safer than having R make assumptions about missing
values!
# Vector with missing values
scores_with_na <- c(85, NA, 92, 78, NA, 88)
# Mean calculation with NA values
mean(scores_with_na) # Returns NA
[1] NA
# Mean calculation excluding NA values
mean(scores_with_na, na.rm = TRUE) # Returns actual mean
[1] 85.75
# Count missing values
sum(is.na(scores_with_na)) # Number of NA values
[1] 2
# Remove NA values
scores_complete <- na.omit(scores_with_na)
length(scores_complete)
[1] 4
Most R functions have an na.rm
parameter to handle
missing values. Setting na.rm = TRUE
tells R to remove NA
values before performing calculations. However, it’s usually better to
clean the dataset of missing values first, and then run functions
without the na.rm
parameter.
# More examples with the starwars dataset
# How many missing heights?
sum(is.na(starwars$height))
[1] 6
# Better: How many are missing, and how many are not?
summary(is.na(starwars$height))
Mode FALSE TRUE
logical 81 6
# WARNING: always use NA-specific functions like is.na() and not equals signs for NA
summary(starwars$height == NA) # Does not work - never use == with NA!
Mode NA's
logical 87
# Average height excluding NA values
mean(starwars$height, na.rm = TRUE)
[1] 174.6049
When working with real data, it’s important to understand why values
are missing and make informed decisions about how to handle them.
Generating Synthetic Data
Example data is important for testing your analysis, getting help
from the R community, or collaborating across organizations. While R
comes with built-in example datasets, they aren’t usually relevant to
education. Creating synthetic (fake) data is an easy and powerful way to
test your code.
Let’s create a simulated dataset of student information:
# Number of students
n <- 50
# Create variables
student_id <- seq(1, n) # Simple sequence of numbers 1 to 50
grade <- sample(c(7,8,9,10,11,12), n, replace = TRUE)
# ^ randomly sample from list of grades, with replacement
secondary <- grade > 8 # TRUE if grade 9 or higher
# Generate test scores
math_score <- round(rnorm(n, mean = 75, sd = 10)) # Normal distribution
english_score <- round(runif(n, min = 50, max = 100)) # Uniform distribution
# Combine into a data frame
synthetic_data <- data.frame(
student_id,
grade,
secondary,
math_score,
english_score
)
# View the first few rows
head(synthetic_data)
Don’t worry about memorizing these functions! Keep a copy of this
code and modify it when you need to generate different types of
synthetic datasets.
Saving and Loading Data on your computer
R can save data in many ways, but you’ll mostly use two formats: R
object files (.RData) and CSV text files (.csv).
R object files
If you need to save data for later use in R, and want to be
absolutely certain that the data will be exactly the same when you load
it back, use R object files:
# Save a single R object
save(synthetic_data, file = "synthetic_data.RData")
# Load the data back
load("synthetic_data.RData")
R object files can contain multiple datasets - they can even save
everything in your current session! The command
save.image()
saves everything in your environment to a file
called .RData
in your project directory. This file is
typically re-loaded next time you start R.
Let’s experiment with the RStudio global environment.
CSV Files
CSV (Comma-Separated Values) files are the most compatible data
format across different programs. They’re just text files where commas
separate columns and new lines separate rows. They look like this:
student_id,grade,math_score
1,9,85
2,10,92
3,9,78
4,11,88
While most programs can read and write CSV files, there are some
common issues to watch for:
- Text data (like names) often needs double-quotes around it,
especially if it contains commas
- Different programs handle column names differently - some use
quotes, some allow spaces
- Mac and Windows computers may save text files slightly
differently
- Even when saving from R and loading back into R, data might change
slightly depending on the options used
Because of these issues, always verify your data after reading a CSV
file, especially when sharing between different programs.
# Save data to CSV
write.csv(synthetic_data, "synthetic_data.csv")
# To read data back, use RStudio's Import Dataset tool:
# File > Import Dataset > From Text (readr)
Reading Data from Remote or Online Sources
R can read data from many remote sources including databases, online
files, and web pages.
We won’t cover database connections in this workshop since they
require special permissions from your IT department. However, once set
up, you will use the same commands we’re learning to work with the
data.
Data files hosted on the web
R can read online files just like local files on your computer. Let’s
load some example data from GitHub:
# Load survey data from URL
survey_data1 <- read.csv("https://github.com/grousell/MISAR/raw/refs/heads/master/Data/survey_data_1.csv")
survey_data2 <- read.csv("https://github.com/grousell/MISAR/raw/refs/heads/master/Data/survey_data_2.csv")
# Look at the first few rows
head(survey_data1)
Notice we use the same read.csv()
function as
before!
Data files in R packages
Many R packages include example datasets:
# install.packages("devtools")
devtools::install_github("grousell/obt")
# library(obt) #optional
head(obt::ele_rc)
To explore datasets in installed packages: 1. Click “Global
Environment” in the Environment pane 2. Select the package name 3. Click
on any dataset to add it to your session 4. Explore it like any other
dataset
The ::
syntax
You may have noticed this new syntax: obt::ele_rc
and
devtools::install_github
This notation is useful in two ways:
Using a single function from a package without loading the whole
package. For example, we only need install_github()
from
devtools once, so we don’t need to load the entire package.
Making it clear where data comes from. When you see
obt::ele_rc
, you know this data comes from the obt package,
not your local environment. This helps avoid confusion when you have
lots of data loaded.
Viewing Data
Let’s explore different ways to examine our synthetic_data:
# Get a quick summary of the data frame
summary(synthetic_data)
student_id grade secondary math_score
Min. : 1.00 Min. : 7.00 Mode :logical Min. : 57.00
1st Qu.:13.25 1st Qu.: 8.00 FALSE:14 1st Qu.: 69.00
Median :25.50 Median :10.00 TRUE :36 Median : 76.50
Mean :25.50 Mean : 9.88 Mean : 75.84
3rd Qu.:37.75 3rd Qu.:12.00 3rd Qu.: 82.75
Max. :50.00 Max. :12.00 Max. :101.00
english_score
Min. :52.00
1st Qu.:63.50
Median :75.00
Mean :74.78
3rd Qu.:83.00
Max. :99.00
# Look at just the first few rows
head(synthetic_data)
# Or the last few rows
tail(synthetic_data)
# Get a glimpse of the structure
glimpse(synthetic_data)
Rows: 50
Columns: 5
$ student_id <int> 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,…
$ grade <dbl> 12, 8, 7, 8, 10, 8, 11, 11, 12, 11, 12, 9, 12, 9, …
$ secondary <lgl> TRUE, FALSE, FALSE, FALSE, TRUE, FALSE, TRUE, TRUE…
$ math_score <dbl> 85, 67, 60, 81, 66, 61, 64, 66, 76, 88, 76, 88, 88…
$ english_score <dbl> 97, 91, 83, 72, 65, 89, 70, 60, 77, 80, 78, 81, 62…
# Open the full data viewer
View(synthetic_data) # Note: Capital V is required!
In the Environment pane, clicking the small “play” button next to
synthetic_data
provides the same information as
glimpse(synthetic_data)
.
For any data frame, clicking the table icon (looks like a grid) in
the Environment pane will open the same viewer as running the
View(synthetic_data)
command.
These tools help us quickly understand our data’s structure and
contents.
Computing new columns
Let’s look at three common ways to compute new information from our
existing data.
Creating calculated columns with mutate()
Sometimes we need to combine information from multiple columns. For
example, we might want to calculate an average score for each
student:
# Add a new column for average score
synthetic_data_with_average <- synthetic_data |>
mutate(average_score = (math_score + english_score) / 2)
head(synthetic_data_with_average)
Let’s read this step by step:
- We start with synthetic_data, THEN
- Create a new column called average_score by:
- Adding math_score and english_score together
- Dividing by 2 to get the average
- Save the entire result to a new data frame called
synthetic_data_with_average
- Print the first few rows to verify it worked
Grouping data for analysis
We often want to analyze data by categories. Let’s look at how scores
vary across grade levels:
# Calculate average scores by grade
synthetic_data |>
group_by(grade) |>
summarise(
avg_math = mean(math_score),
avg_english = mean(english_score)
)
Reading the pipe: Start with synthetic_data, THEN group the rows by
grade level, THEN calculate summary statistics (average math and english
scores) for each grade group.
Counting observations in groups
Sometimes we just need to know how many items fall into different
categories:
# Count students in each grade
synthetic_data |>
group_by(grade) |>
summarise(n_students = n())
Reading the pipe: Start with synthetic_data, THEN group the rows by
grade level, THEN count how many students are in each grade using the
n() function.
Data Cleaning
When we load data from external sources, we often need to “clean” it
before we can use it for analysis. Common data cleaning tasks
include:
- Fixing inconsistent data entry (e.g., “Grade 9” vs “9” vs
“Nine”)
- Handling missing values (empty cells or special codes like -99 need
to be converted to
NA
)
- Removing duplicate entries
- Correcting data types (e.g., numbers stored as text)
- Standardizing column names and values
- Identifying and handling outliers
- Dealing with extra spaces or special characters
Data cleaning is often the most time-consuming part of data analysis,
but it’s crucial for getting accurate results. Let’s look at some common
data cleaning tasks using our survey data:
Handling correct data types
Above, we imported survey_data1
and
survey_data2
. Open those dataframes using the Environment
pane and take a look at the types. Everything was imported as
characters! The reason is that the numerical responses include some
text, and R, to avoid damaging the data by trying to convert to numbers,
just imported everything as text.
# Take a look at one variable
survey_data2$session_knowledge |>
unique() # Show only the unique values
[1] "4" "3" "2"
[4] "5 - Significantly" "1 - Not at All" NA
We can see the values are numeric, from a Likert-type scale. We
usually consider this type of data to be ordinal, rather than continous,
and in R, that is a special variable type called an ordered factor.
survey_data2 <- survey_data2 |>
mutate(
session_knowledge = factor(
session_knowledge,
levels = c("1 - Not at All", "2", "3", "4", "5 - Significantly"),
ordered = TRUE
)
)
# Check the result
survey_data2$session_knowledge |>
levels()
[1] "1 - Not at All" "2" "3"
[4] "4" "5 - Significantly"
Now, we have kept the combination of text and numbers, but the data
is now ordered. This will be useful later if we try to plot the data.
For example, a bar plot would automatically put the 5 levels in the
correct order, and the helpful text would be part of the legend.
If we really wanted this data to be numeric (perhaps the scale truly
represents a continuous variable), we can extract just the numbers and
convert it to a numeric variable:
# Extract just the first character and convert to numeric
survey_data2 <- survey_data2 |>
mutate(
session_knowledge_num = substr(session_knowledge, 1, 1) |>
as.numeric()
)
# Now we can perform computations as a numerical vector
mean(survey_data2$session_knowledge_num, na.rm = TRUE)
[1] 3.644628
Note that converting ordinal data to numeric should be done carefully
and with purpose. While it can enable additional statistical operations,
it assumes the distances between levels are equal and meaningful. We
have kept the origional ordered factor and created a seperate numeric
version. Even if the numeric version is inappropriate to the categorical
Likert-type response, it may still be useful for finding outliers. We
could create numerical versions of all the items, and add the columns to
identify extremes.
Correcting inconsistent data entry and missing values
Let’s create a small dataset with some common data problems:
# Create messy data
messy_data <- data.frame(
student_id = 1:5,
grade = c("Grade 9", "9", "grade 9", "Grade 9", "-99"),
score = c(85, 92, 78, -99, 88)
)
# View the messy data
messy_data
# Step 1: Fix inconsistent grade entries that start with "Grade"
clean_data <- messy_data |>
mutate(
grade = str_replace(grade, "Grade 9", "9")
)
# Step 2: Fix grades that start with lowercase "grade"
clean_data <- clean_data |>
mutate(
grade = str_replace(grade, "grade 9", "9")
)
# Step 3: Replace -99 with NA in both grade and score
clean_data <- clean_data |>
mutate(
grade = if_else(grade == "-99", NA_character_, grade),
score = if_else(score == -99, NA_real_, score)
)
# View the cleaned data
clean_data
In this example, we used:
str_replace()
to handle text replacements in the grade
column
if_else()
to replace specific values with NA in the
score column
NA_character_
and NA_real_
to specify the
type of NA value
Removing duplicates
Let’s create a small dataset with some duplicate entries:
# Create data with duplicates
duplicate_data <- tibble(
ResponseID = c(101, 101, 102, 103, 103),
date = c("2023-01-15", "2023-01-15", "2023-02-01", "2023-01-01", "2023-02-01"),
score = c(85, 85, 92, 78, 82)
)
# View the data
duplicate_data
# Remove exact duplicates (Entire entry is a duplicate)
unique_data <- duplicate_data |>
distinct()
# View result after removing exact duplicates
unique_data
# Keep only most recent entry for each ResponseID (e.g., someone participated twice)
latest_data <- duplicate_data |>
group_by(ResponseID) |>
slice_max(date) |>
ungroup()
# View final result
latest_data
In this example, we first remove exact duplicates with
distinct()
, then use slice_max()
to keep only
the most recent entry for each ResponseID. This is a common task when
dealing with multiple responses from the same participant.
Identifying Outliers
Let’s add some outliers to our synthetic data and then identify
them:
# Add some outlier math scores
synthetic_data$math_score[1:3] <- c(15, 20, 125) # Add unrealistic scores
# Calculate summary statistics
outlier_analysis <- synthetic_data |>
summarise(
q1 = quantile(math_score, 0.25),
q3 = quantile(math_score, 0.75),
iqr = q3 - q1,
lower_bound = q1 - 1.5 * iqr,
upper_bound = q3 + 1.5 * iqr
)
# Find outliers
outliers <- synthetic_data |>
filter(
math_score < outlier_analysis$lower_bound |
math_score > outlier_analysis$upper_bound
)
# View the outliers
outliers |>
select(student_id, math_score)
# Remove outliers from our data
synthetic_data_clean <- synthetic_data |>
filter(
math_score > outlier_analysis$lower_bound,
math_score < outlier_analysis$upper_bound
)
# Verify the outliers are gone
summary(synthetic_data_clean$math_score)
Min. 1st Qu. Median Mean 3rd Qu. Max.
57.00 69.50 77.00 76.17 82.50 101.00
This example uses the Interquartile Range (IQR) method to identify
outliers. Values that fall more than 1.5 times the IQR below the first
quartile or above the third quartile are considered outliers. When
dealing with real data, you should investigate outliers carefully - they
might represent data entry errors, or they might be valid but unusual
cases.
Joining Data
Sometimes we need to combine two different datasets. For example, you
might have: - Student demographic information in one spreadsheet - Test
scores in another spreadsheet - Survey responses in yet another
In Excel, you might use VLOOKUP to match information between
spreadsheets. In R and most Database systems, we use “joins” to combine
datasets based on a shared identifier column, like student number. This
is also how Microsoft Access and PowerBI combine tables as well.
Let’s look at our survey data:
# Look at the first few rows of each dataset
head(survey_data1)
Both datasets have a RespondentID column that we can use to match
rows between the datasets. However, not every respondent appears in both
datasets:
# Which respondents are only in dataset 1?
survey_data1 |>
filter(!RespondentID %in% survey_data2$RespondentID) |>
pull(RespondentID) |>
length()
[1] 13
# Which respondents are only in dataset 2?
survey_data2 |>
filter(!RespondentID %in% survey_data1$RespondentID) |>
pull(RespondentID) |>
length()
[1] 0
There are four main ways to join datasets:
- Inner join: Keep only rows that match in both datasets
- Left join: Keep all rows from the first dataset, add matching data
from the second
- Right join: Keep all rows from the second dataset, add matching data
from the first
- Full join: Keep all rows from both datasets
# Inner join - only matching rows
inner_joined <- survey_data1 |>
inner_join(survey_data2, by = "RespondentID")
# Left join - all rows from survey_data1
left_joined <- survey_data1 |>
left_join(survey_data2, by = "RespondentID")
# Compare the number of rows
nrow(inner_joined) # Fewer rows - only matching RespondentIDs
[1] 252
nrow(left_joined) # Same number of rows as survey_data1
[1] 265
Usually, you’ll use left_join() to add information to your main
dataset. Any respondents that don’t have matching data will get NA
values in the columns from the second dataset. Take a look at
left_joined
to see what I mean.
End of Module 3
In this module, we’ve covered a lot of ground! You now understand how
to:
- Work with built-in datasets and create synthetic data for
testing
- Handle missing data with NA values
- Load data from files and online sources
- View and explore data in RStudio
- Extract specific parts of your data using both base R and tidyverse
approaches
- Clean messy data and handle common data problems
- Join datasets together using different types of joins
Congratulations on making it through the most challenging part! Data
cleaning and preparation typically takes up most of our time in
real-world analysis, but having these skills means you can work with
almost any dataset in R and RStudio.
In the next module, we’ll focus on something more rewarding -
creating high-quality figures and reports using R Markdown. You’ll learn
how to present your analysis professionally and create reproducible
documents that combine text, code, and visualizations.
LS0tCnRpdGxlOiAiTW9kdWxlIDI6IEhhbmRsaW5nIERhdGEgaW4gUiIKYXV0aG9yOiAiSmFzb24gTG9ja2xpbiIKZGF0ZTogIk5vdmVtYmVyIDEyLCAyMDI0IgpvdXRwdXQ6CiAgaHRtbF9ub3RlYm9vazoKICAgIHRvYzogdHJ1ZQogICAgdG9jX2Zsb2F0OiB0cnVlCiAgICB0aGVtZTogdW5pdGVkCiAgICBoaWdobGlnaHQ6IHRhbmdvCmVkaXRvcl9vcHRpb25zOgogIG1hcmtkb3duOgogICAgd3JhcDogNzIKLS0tCgpgYGB7ciBzZXR1cH0Ka25pdHI6Om9wdHNfY2h1bmskc2V0KAogZWNobyA9IFRSVUUsCiBtZXNzYWdlID0gRkFMU0UsCiB3YXJuaW5nID0gRkFMU0UKKQpsb2FkKCIuUkRhdGEiKQpgYGAKCiMgTW9kdWxlIDI6IEhhbmRsaW5nIERhdGEgaW4gUgoKTm93IHRoYXQgd2UgaGF2ZSBhIGJhc2ljIHVuZGVyc3RhbmRpbmcgb2YgUidzIHN5bnRheCBhbmQgdGhlIFJTdHVkaW8gYXBwbGljYXRpb24sIHdlIGNhbiBzdGFydCB3b3JraW5nIHdpdGggZGF0YSEKClRoaXMgbW9kdWxlIHdpbGwgY292ZXI6CgotIFdvcmtpbmcgd2l0aCBidWlsdC1pbiBkYXRhc2V0cyBpbiBSCi0gVW5kZXJzdGFuZGluZyBhbmQgaGFuZGxpbmcgbWlzc2luZyBkYXRhIChgTkFgIHZhbHVlcykKLSBDcmVhdGluZyBzeW50aGV0aWMgZGF0YXNldHMgZm9yIHRlc3RpbmcgYW5kIGV4YW1wbGVzCi0gRGlmZmVyZW50IHdheXMgdG8gdmlldyBhbmQgZXhwbG9yZSBkYXRhIGluIFJTdHVkaW8KLSBFeHRyYWN0aW5nIHNwZWNpZmljIGRhdGEgZnJvbSB2ZWN0b3JzIGFuZCBkYXRhIGZyYW1lcwotIExvYWRpbmcgYW5kIHNhdmluZyBkYXRhIGxvY2FsbHkgYW5kIG9ubGluZQotIENvbW1vbiBwaXRmYWxscyBhbmQgZ290Y2hhcyB3aGVuIHdvcmtpbmcgd2l0aCBSCgpCeSB0aGUgZW5kIG9mIHRoaXMgbW9kdWxlLCB5b3UnbGwgYmUgY29tZm9ydGFibGUgbG9hZGluZyBkYXRhLCBpbnNwZWN0aW5nIGl0IGZvciBwcm9ibGVtcywgYW5kIGV4dHJhY3RpbmcgdGhlIHBhcnRzIHlvdSBuZWVkIGZvciBhbmFseXNpcy4gV2UnbGwgdXNlIGJvdGggYmFzZSBSIGFuZCB0aWR5dmVyc2UgYXBwcm9hY2hlcywgaGVscGluZyB5b3UgdW5kZXJzdGFuZCBib3RoIHRoZSB0cmFkaXRpb25hbCBhbmQgbW9kZXJuIHdheXMgb2Ygd29ya2luZyB3aXRoIGRhdGEgaW4gUi4KCiMjIEJ1aWx0LWluIERhdGFzZXRzCgpNYW55IFIgcGFja2FnZXMgaW5jbHVkZSBleGFtcGxlIGRhdGFzZXRzLiBMZXQncyBsb29rIGF0IHR3bzoKCmBgYHtyfQojIFZpZXcgdGhlIGZpcnN0IGZldyByb3dzIG9mIHRoZSBzdGFyd2FycyBkYXRhIHNldApsaWJyYXJ5KGRwbHlyKQpoZWFkKHN0YXJ3YXJzKQoKIyBBbm90aGVyIGRhdGFzZXQgdGhhdCBpbmNsdWRlcyB0aGUgZnVlbCBlZmZpY2llbmN5IG9mIDMyIGNhcnMuCm1lYW4obXRjYXJzJG1wZykKYGBgCgpUaGVzZSBkYXRhc2V0cyBhcmUgZnJlcXVlbnRseSB1c2VkIGluIGhlbHAgZmlsZXMsIG9ubGluZSBkaXNjdXNzaW9ucywgb3IgYmxvZyBwb3N0cyBkZW1vbnN0cmF0aW5nIFIgZnVuY3Rpb25zLgoKTm90aWNlIHRoZSBgTkFgIGhhaXIgY29sb3IgaW4gdGhlIHRhYmxlPyBUaGF0J3MgaG93IFIgcmVwcmVzZW50cyBtaXNzaW5nIGRhdGEuCgojIyBNaXNzaW5nIERhdGEgaW4gUgoKUiB1c2VzIGBOQWAgKE5vdCBBdmFpbGFibGUpIHRvIHJlcHJlc2VudCBtaXNzaW5nIHZhbHVlcy4gVGhlc2UgYXJlIGRpZmZlcmVudCBmcm9tIHplcm9zIG9yIGVtcHR5IHN0cmluZ3MuIFdoZW4geW91IHBlcmZvcm0gY2FsY3VsYXRpb25zIHdpdGggYE5BYCB2YWx1ZXMsIHRoZSByZXN1bHQgd2lsbCBiZSBgTkFgIHVubGVzcyB5b3UgZXhwbGljaXRseSB0ZWxsIFIgaG93IHRvIGhhbmRsZSB0aGUgbWlzc2luZyBkYXRhLiBUaGlzIGlzIHNhZmVyIHRoYW4gaGF2aW5nIFIgbWFrZSBhc3N1bXB0aW9ucyBhYm91dCBtaXNzaW5nIHZhbHVlcyEKCmBgYHtyfQojIFZlY3RvciB3aXRoIG1pc3NpbmcgdmFsdWVzCnNjb3Jlc193aXRoX25hIDwtIGMoODUsIE5BLCA5MiwgNzgsIE5BLCA4OCkKCiMgTWVhbiBjYWxjdWxhdGlvbiB3aXRoIE5BIHZhbHVlcwptZWFuKHNjb3Jlc193aXRoX25hKSAgIyBSZXR1cm5zIE5BCgojIE1lYW4gY2FsY3VsYXRpb24gZXhjbHVkaW5nIE5BIHZhbHVlcwptZWFuKHNjb3Jlc193aXRoX25hLCBuYS5ybSA9IFRSVUUpICAjIFJldHVybnMgYWN0dWFsIG1lYW4KCiMgQ291bnQgbWlzc2luZyB2YWx1ZXMKc3VtKGlzLm5hKHNjb3Jlc193aXRoX25hKSkgICMgTnVtYmVyIG9mIE5BIHZhbHVlcwoKIyBSZW1vdmUgTkEgdmFsdWVzCnNjb3Jlc19jb21wbGV0ZSA8LSBuYS5vbWl0KHNjb3Jlc193aXRoX25hKQpsZW5ndGgoc2NvcmVzX2NvbXBsZXRlKQpgYGAKCk1vc3QgUiBmdW5jdGlvbnMgaGF2ZSBhbiBgbmEucm1gIHBhcmFtZXRlciB0byBoYW5kbGUgbWlzc2luZyB2YWx1ZXMuIFNldHRpbmcgYG5hLnJtID0gVFJVRWAgdGVsbHMgUiB0byByZW1vdmUgTkEgdmFsdWVzIGJlZm9yZSBwZXJmb3JtaW5nIGNhbGN1bGF0aW9ucy4gSG93ZXZlciwgaXQncyB1c3VhbGx5IGJldHRlciB0byBjbGVhbiB0aGUgZGF0YXNldCBvZiBtaXNzaW5nIHZhbHVlcyBmaXJzdCwgYW5kIHRoZW4gcnVuIGZ1bmN0aW9ucyB3aXRob3V0IHRoZSBgbmEucm1gIHBhcmFtZXRlci4KCmBgYHtyfQojIE1vcmUgZXhhbXBsZXMgd2l0aCB0aGUgc3RhcndhcnMgZGF0YXNldAojIEhvdyBtYW55IG1pc3NpbmcgaGVpZ2h0cz8Kc3VtKGlzLm5hKHN0YXJ3YXJzJGhlaWdodCkpCgojIEJldHRlcjogSG93IG1hbnkgYXJlIG1pc3NpbmcsIGFuZCBob3cgbWFueSBhcmUgbm90PwpzdW1tYXJ5KGlzLm5hKHN0YXJ3YXJzJGhlaWdodCkpCgojIFdBUk5JTkc6IGFsd2F5cyB1c2UgTkEtc3BlY2lmaWMgZnVuY3Rpb25zIGxpa2UgaXMubmEoKSBhbmQgbm90IGVxdWFscyBzaWducyBmb3IgTkEKc3VtbWFyeShzdGFyd2FycyRoZWlnaHQgPT0gTkEpICMgRG9lcyBub3Qgd29yayAtIG5ldmVyIHVzZSA9PSB3aXRoIE5BIQoKIyBBdmVyYWdlIGhlaWdodCBleGNsdWRpbmcgTkEgdmFsdWVzCm1lYW4oc3RhcndhcnMkaGVpZ2h0LCBuYS5ybSA9IFRSVUUpCmBgYAoKV2hlbiB3b3JraW5nIHdpdGggcmVhbCBkYXRhLCBpdCdzIGltcG9ydGFudCB0byB1bmRlcnN0YW5kIHdoeSB2YWx1ZXMgYXJlIG1pc3NpbmcgYW5kIG1ha2UgaW5mb3JtZWQgZGVjaXNpb25zIGFib3V0IGhvdyB0byBoYW5kbGUgdGhlbS4KCiMjIEdlbmVyYXRpbmcgU3ludGhldGljIERhdGEKCkV4YW1wbGUgZGF0YSBpcyBpbXBvcnRhbnQgZm9yIHRlc3RpbmcgeW91ciBhbmFseXNpcywgZ2V0dGluZyBoZWxwIGZyb20gdGhlIFIgY29tbXVuaXR5LCBvciBjb2xsYWJvcmF0aW5nIGFjcm9zcyBvcmdhbml6YXRpb25zLiBXaGlsZSBSIGNvbWVzIHdpdGggYnVpbHQtaW4gZXhhbXBsZSBkYXRhc2V0cywgdGhleSBhcmVuJ3QgdXN1YWxseSByZWxldmFudCB0byBlZHVjYXRpb24uIENyZWF0aW5nIHN5bnRoZXRpYyAoZmFrZSkgZGF0YSBpcyBhbiBlYXN5IGFuZCBwb3dlcmZ1bCB3YXkgdG8gdGVzdCB5b3VyIGNvZGUuCgpMZXQncyBjcmVhdGUgYSBzaW11bGF0ZWQgZGF0YXNldCBvZiBzdHVkZW50IGluZm9ybWF0aW9uOgoKYGBge3J9CiMgTnVtYmVyIG9mIHN0dWRlbnRzCm4gPC0gNTAKCiMgQ3JlYXRlIHZhcmlhYmxlcwpzdHVkZW50X2lkIDwtIHNlcSgxLCBuKSAjIFNpbXBsZSBzZXF1ZW5jZSBvZiBudW1iZXJzIDEgdG8gNTAKZ3JhZGUgPC0gc2FtcGxlKGMoNyw4LDksMTAsMTEsMTIpLCBuLCByZXBsYWNlID0gVFJVRSkKICAgICAgICAjIF4gcmFuZG9tbHkgc2FtcGxlIGZyb20gbGlzdCBvZiBncmFkZXMsIHdpdGggcmVwbGFjZW1lbnQKc2Vjb25kYXJ5IDwtIGdyYWRlID4gOCAgIyBUUlVFIGlmIGdyYWRlIDkgb3IgaGlnaGVyCgojIEdlbmVyYXRlIHRlc3Qgc2NvcmVzCm1hdGhfc2NvcmUgPC0gcm91bmQocm5vcm0obiwgbWVhbiA9IDc1LCBzZCA9IDEwKSkgIyBOb3JtYWwgZGlzdHJpYnV0aW9uCmVuZ2xpc2hfc2NvcmUgPC0gcm91bmQocnVuaWYobiwgbWluID0gNTAsIG1heCA9IDEwMCkpICMgVW5pZm9ybSBkaXN0cmlidXRpb24KCiMgQ29tYmluZSBpbnRvIGEgZGF0YSBmcmFtZQpzeW50aGV0aWNfZGF0YSA8LSBkYXRhLmZyYW1lKAogIHN0dWRlbnRfaWQsCiAgZ3JhZGUsCiAgc2Vjb25kYXJ5LAogIG1hdGhfc2NvcmUsCiAgZW5nbGlzaF9zY29yZQopCgojIFZpZXcgdGhlIGZpcnN0IGZldyByb3dzCmhlYWQoc3ludGhldGljX2RhdGEpCmBgYAoKRG9uJ3Qgd29ycnkgYWJvdXQgbWVtb3JpemluZyB0aGVzZSBmdW5jdGlvbnMhIEtlZXAgYSBjb3B5IG9mIHRoaXMgY29kZSBhbmQgbW9kaWZ5IGl0IHdoZW4geW91IG5lZWQgdG8gZ2VuZXJhdGUgZGlmZmVyZW50IHR5cGVzIG9mIHN5bnRoZXRpYyBkYXRhc2V0cy4KCgojIyBTYXZpbmcgYW5kIExvYWRpbmcgRGF0YSBvbiB5b3VyIGNvbXB1dGVyCgpSIGNhbiBzYXZlIGRhdGEgaW4gbWFueSB3YXlzLCBidXQgeW91J2xsIG1vc3RseSB1c2UgdHdvIGZvcm1hdHM6IFIgb2JqZWN0IGZpbGVzICguUkRhdGEpIGFuZCBDU1YgdGV4dCBmaWxlcyAoLmNzdikuCgojIyMgUiBvYmplY3QgZmlsZXMKSWYgeW91IG5lZWQgdG8gc2F2ZSBkYXRhIGZvciBsYXRlciB1c2UgaW4gUiwgYW5kIHdhbnQgdG8gYmUgYWJzb2x1dGVseSBjZXJ0YWluIHRoYXQgdGhlIGRhdGEgd2lsbCBiZSBleGFjdGx5IHRoZSBzYW1lIHdoZW4geW91IGxvYWQgaXQgYmFjaywgdXNlIFIgb2JqZWN0IGZpbGVzOgoKYGBge3J9CiMgU2F2ZSBhIHNpbmdsZSBSIG9iamVjdApzYXZlKHN5bnRoZXRpY19kYXRhLCBmaWxlID0gInN5bnRoZXRpY19kYXRhLlJEYXRhIikKCiMgTG9hZCB0aGUgZGF0YSBiYWNrCmxvYWQoInN5bnRoZXRpY19kYXRhLlJEYXRhIikKYGBgCgpSIG9iamVjdCBmaWxlcyBjYW4gY29udGFpbiBtdWx0aXBsZSBkYXRhc2V0cyAtIHRoZXkgY2FuIGV2ZW4gc2F2ZSBldmVyeXRoaW5nIGluIHlvdXIgY3VycmVudCBzZXNzaW9uISBUaGUgY29tbWFuZCBgc2F2ZS5pbWFnZSgpYCBzYXZlcyBldmVyeXRoaW5nIGluIHlvdXIgZW52aXJvbm1lbnQgdG8gYSBmaWxlIGNhbGxlZCBgLlJEYXRhYCBpbiB5b3VyIHByb2plY3QgZGlyZWN0b3J5LiBUaGlzIGZpbGUgaXMgdHlwaWNhbGx5IHJlLWxvYWRlZCBuZXh0IHRpbWUgeW91IHN0YXJ0IFIuCgpMZXQncyBleHBlcmltZW50IHdpdGggdGhlIFJTdHVkaW8gZ2xvYmFsIGVudmlyb25tZW50LgoKIyMjIENTViBGaWxlcwpDU1YgKENvbW1hLVNlcGFyYXRlZCBWYWx1ZXMpIGZpbGVzIGFyZSB0aGUgbW9zdCBjb21wYXRpYmxlIGRhdGEgZm9ybWF0IGFjcm9zcyBkaWZmZXJlbnQgcHJvZ3JhbXMuIFRoZXkncmUganVzdCB0ZXh0IGZpbGVzIHdoZXJlIGNvbW1hcyBzZXBhcmF0ZSBjb2x1bW5zIGFuZCBuZXcgbGluZXMgc2VwYXJhdGUgcm93cy4gVGhleSBsb29rIGxpa2UgdGhpczoKCmBgYHR4dApzdHVkZW50X2lkLGdyYWRlLG1hdGhfc2NvcmUKMSw5LDg1CjIsMTAsOTIKMyw5LDc4CjQsMTEsODgKYGBgCgpXaGlsZSBtb3N0IHByb2dyYW1zIGNhbiByZWFkIGFuZCB3cml0ZSBDU1YgZmlsZXMsIHRoZXJlIGFyZSBzb21lIGNvbW1vbiBpc3N1ZXMgdG8gd2F0Y2ggZm9yOgoKLSBUZXh0IGRhdGEgKGxpa2UgbmFtZXMpIG9mdGVuIG5lZWRzIGRvdWJsZS1xdW90ZXMgYXJvdW5kIGl0LCBlc3BlY2lhbGx5IGlmIGl0IGNvbnRhaW5zIGNvbW1hcwotIERpZmZlcmVudCBwcm9ncmFtcyBoYW5kbGUgY29sdW1uIG5hbWVzIGRpZmZlcmVudGx5IC0gc29tZSB1c2UgcXVvdGVzLCBzb21lIGFsbG93IHNwYWNlcwotIE1hYyBhbmQgV2luZG93cyBjb21wdXRlcnMgbWF5IHNhdmUgdGV4dCBmaWxlcyBzbGlnaHRseSBkaWZmZXJlbnRseQotIEV2ZW4gd2hlbiBzYXZpbmcgZnJvbSBSIGFuZCBsb2FkaW5nIGJhY2sgaW50byBSLCBkYXRhIG1pZ2h0IGNoYW5nZSBzbGlnaHRseSBkZXBlbmRpbmcgb24gdGhlIG9wdGlvbnMgdXNlZAoKQmVjYXVzZSBvZiB0aGVzZSBpc3N1ZXMsIGFsd2F5cyB2ZXJpZnkgeW91ciBkYXRhIGFmdGVyIHJlYWRpbmcgYSBDU1YgZmlsZSwgZXNwZWNpYWxseSB3aGVuIHNoYXJpbmcgYmV0d2VlbiBkaWZmZXJlbnQgcHJvZ3JhbXMuCgpgYGB7cn0KIyBTYXZlIGRhdGEgdG8gQ1NWCndyaXRlLmNzdihzeW50aGV0aWNfZGF0YSwgInN5bnRoZXRpY19kYXRhLmNzdiIpCgojIFRvIHJlYWQgZGF0YSBiYWNrLCB1c2UgUlN0dWRpbydzIEltcG9ydCBEYXRhc2V0IHRvb2w6CiMgRmlsZSA+IEltcG9ydCBEYXRhc2V0ID4gRnJvbSBUZXh0IChyZWFkcikKYGBgCgoKIyMgUmVhZGluZyBEYXRhIGZyb20gUmVtb3RlIG9yIE9ubGluZSBTb3VyY2VzCgpSIGNhbiByZWFkIGRhdGEgZnJvbSBtYW55IHJlbW90ZSBzb3VyY2VzIGluY2x1ZGluZyBkYXRhYmFzZXMsIG9ubGluZSBmaWxlcywgYW5kIHdlYiBwYWdlcy4KCldlIHdvbid0IGNvdmVyIGRhdGFiYXNlIGNvbm5lY3Rpb25zIGluIHRoaXMgd29ya3Nob3Agc2luY2UgdGhleSByZXF1aXJlIHNwZWNpYWwgcGVybWlzc2lvbnMgZnJvbSB5b3VyIElUIGRlcGFydG1lbnQuIEhvd2V2ZXIsIG9uY2Ugc2V0IHVwLCB5b3Ugd2lsbCB1c2UgdGhlIHNhbWUgY29tbWFuZHMgd2UncmUgbGVhcm5pbmcgdG8gd29yayB3aXRoIHRoZSBkYXRhLgoKIyMjIERhdGEgZmlsZXMgaG9zdGVkIG9uIHRoZSB3ZWIKUiBjYW4gcmVhZCBvbmxpbmUgZmlsZXMganVzdCBsaWtlIGxvY2FsIGZpbGVzIG9uIHlvdXIgY29tcHV0ZXIuIExldCdzIGxvYWQgc29tZSBleGFtcGxlIGRhdGEgZnJvbSBHaXRIdWI6CgpgYGB7cn0KIyBMb2FkIHN1cnZleSBkYXRhIGZyb20gVVJMCnN1cnZleV9kYXRhMSA8LSByZWFkLmNzdigiaHR0cHM6Ly9naXRodWIuY29tL2dyb3VzZWxsL01JU0FSL3Jhdy9yZWZzL2hlYWRzL21hc3Rlci9EYXRhL3N1cnZleV9kYXRhXzEuY3N2IikKCnN1cnZleV9kYXRhMiA8LSByZWFkLmNzdigiaHR0cHM6Ly9naXRodWIuY29tL2dyb3VzZWxsL01JU0FSL3Jhdy9yZWZzL2hlYWRzL21hc3Rlci9EYXRhL3N1cnZleV9kYXRhXzIuY3N2IikKCiMgTG9vayBhdCB0aGUgZmlyc3QgZmV3IHJvd3MKaGVhZChzdXJ2ZXlfZGF0YTEpCgpgYGAKCk5vdGljZSB3ZSB1c2UgdGhlIHNhbWUgYHJlYWQuY3N2KClgIGZ1bmN0aW9uIGFzIGJlZm9yZSEKCiMjIyBEYXRhIGZpbGVzIGluIFIgcGFja2FnZXMKCk1hbnkgUiBwYWNrYWdlcyBpbmNsdWRlIGV4YW1wbGUgZGF0YXNldHM6CgpgYGB7cn0KIyBpbnN0YWxsLnBhY2thZ2VzKCJkZXZ0b29scyIpCmRldnRvb2xzOjppbnN0YWxsX2dpdGh1YigiZ3JvdXNlbGwvb2J0IikKIyBsaWJyYXJ5KG9idCkgI29wdGlvbmFsCmhlYWQob2J0OjplbGVfcmMpCmBgYAoKVG8gZXhwbG9yZSBkYXRhc2V0cyBpbiBpbnN0YWxsZWQgcGFja2FnZXM6CjEuIENsaWNrICJHbG9iYWwgRW52aXJvbm1lbnQiIGluIHRoZSBFbnZpcm9ubWVudCBwYW5lCjIuIFNlbGVjdCB0aGUgcGFja2FnZSBuYW1lCjMuIENsaWNrIG9uIGFueSBkYXRhc2V0IHRvIGFkZCBpdCB0byB5b3VyIHNlc3Npb24KNC4gRXhwbG9yZSBpdCBsaWtlIGFueSBvdGhlciBkYXRhc2V0CgojIyMjIFRoZSBgOjpgIHN5bnRheApZb3UgbWF5IGhhdmUgbm90aWNlZCB0aGlzIG5ldyBzeW50YXg6IGBvYnQ6OmVsZV9yY2AgYW5kIGBkZXZ0b29sczo6aW5zdGFsbF9naXRodWJgCgpUaGlzIG5vdGF0aW9uIGlzIHVzZWZ1bCBpbiB0d28gd2F5czoKCjEuIFVzaW5nIGEgc2luZ2xlIGZ1bmN0aW9uIGZyb20gYSBwYWNrYWdlIHdpdGhvdXQgbG9hZGluZyB0aGUgd2hvbGUgcGFja2FnZS4gRm9yIGV4YW1wbGUsIHdlIG9ubHkgbmVlZCBgaW5zdGFsbF9naXRodWIoKWAgZnJvbSBkZXZ0b29scyBvbmNlLCBzbyB3ZSBkb24ndCBuZWVkIHRvIGxvYWQgdGhlIGVudGlyZSBwYWNrYWdlLgoKMi4gTWFraW5nIGl0IGNsZWFyIHdoZXJlIGRhdGEgY29tZXMgZnJvbS4gV2hlbiB5b3Ugc2VlIGBvYnQ6OmVsZV9yY2AsIHlvdSBrbm93IHRoaXMgZGF0YSBjb21lcyBmcm9tIHRoZSBvYnQgcGFja2FnZSwgbm90IHlvdXIgbG9jYWwgZW52aXJvbm1lbnQuIFRoaXMgaGVscHMgYXZvaWQgY29uZnVzaW9uIHdoZW4geW91IGhhdmUgbG90cyBvZiBkYXRhIGxvYWRlZC4KCgojIyBWaWV3aW5nIERhdGEKCkxldCdzIGV4cGxvcmUgZGlmZmVyZW50IHdheXMgdG8gZXhhbWluZSBvdXIgc3ludGhldGljX2RhdGE6CgpgYGB7cn0KIyBHZXQgYSBxdWljayBzdW1tYXJ5IG9mIHRoZSBkYXRhIGZyYW1lCnN1bW1hcnkoc3ludGhldGljX2RhdGEpCgojIExvb2sgYXQganVzdCB0aGUgZmlyc3QgZmV3IHJvd3MKaGVhZChzeW50aGV0aWNfZGF0YSkKCiMgT3IgdGhlIGxhc3QgZmV3IHJvd3MKdGFpbChzeW50aGV0aWNfZGF0YSkKCiMgR2V0IGEgZ2xpbXBzZSBvZiB0aGUgc3RydWN0dXJlCmdsaW1wc2Uoc3ludGhldGljX2RhdGEpCgojIE9wZW4gdGhlIGZ1bGwgZGF0YSB2aWV3ZXIKVmlldyhzeW50aGV0aWNfZGF0YSkgICMgTm90ZTogQ2FwaXRhbCBWIGlzIHJlcXVpcmVkIQpgYGAKCkluIHRoZSBFbnZpcm9ubWVudCBwYW5lLCBjbGlja2luZyB0aGUgc21hbGwgInBsYXkiIGJ1dHRvbiBuZXh0IHRvIGBzeW50aGV0aWNfZGF0YWAgcHJvdmlkZXMgdGhlIHNhbWUgaW5mb3JtYXRpb24gYXMgYGdsaW1wc2Uoc3ludGhldGljX2RhdGEpYC4KCkZvciBhbnkgZGF0YSBmcmFtZSwgY2xpY2tpbmcgdGhlIHRhYmxlIGljb24gKGxvb2tzIGxpa2UgYSBncmlkKSBpbiB0aGUgRW52aXJvbm1lbnQgcGFuZSB3aWxsIG9wZW4gdGhlIHNhbWUgdmlld2VyIGFzIHJ1bm5pbmcgdGhlIGBWaWV3KHN5bnRoZXRpY19kYXRhKWAgY29tbWFuZC4KClRoZXNlIHRvb2xzIGhlbHAgdXMgcXVpY2tseSB1bmRlcnN0YW5kIG91ciBkYXRhJ3Mgc3RydWN0dXJlIGFuZCBjb250ZW50cy4KCiMjIEV4dHJhY3RpbmcgRGF0YQoKIyMjIFdvcmtpbmcgd2l0aCBWZWN0b3JzCgpSZW1lbWJlciB0aGF0IGEgdmVjdG9yIGlzIGp1c3QgYSBzaW5nbGUgbGlzdCBvZiBvbmUgdHlwZSBvZiBkYXRhIChsaWtlIG51bWJlcnMgb3IgdGV4dCkuIExldCdzIGxvb2sgYXQgdGhlIG1hdGhfc2NvcmUgdmVjdG9yOgoKVGhlIGJhc2UgUiB3YXk6CmBgYHtyfQojIEV4dHJhY3QgdGhlIGZpcnN0IHZhbHVlIGluIG1hdGhfc2NvcmUKc3ludGhldGljX2RhdGEkbWF0aF9zY29yZVsxXQoKIyBFeHRyYWN0IHRoZSBmaXJzdCB0aHJlZSB2YWx1ZXMgb2YgbWF0aF9zY29yZQpzeW50aGV0aWNfZGF0YSRtYXRoX3Njb3JlWzE6M10KCiMgRXh0cmFjdCBhbGwgbWF0aCBzY29yZXMgb3ZlciA4MApzeW50aGV0aWNfZGF0YSRtYXRoX3Njb3JlW3N5bnRoZXRpY19kYXRhJG1hdGhfc2NvcmUgPiA4MF0KYGBgCgoKVGhlIHRpZHl2ZXJzZSB3YXk6CmBgYHtyfQpzeW50aGV0aWNfZGF0YSB8PgogIHB1bGwobWF0aF9zY29yZSkgfD4KICBoZWFkKDMpCgpzeW50aGV0aWNfZGF0YSB8PgogIGZpbHRlcihtYXRoX3Njb3JlID4gODApIHw+CiAgcHVsbChtYXRoX3Njb3JlKQpgYGAKQWx3YXlzIHJlbWVtYmVyIHRvIHJlYWQgdGhlIHBpcGUgc3ltYm9sIGB8PmAgYXMgIlRIRU4uIiBJbiB0aGUgZmlyc3QgZXhhbXBsZSwgeW91IGNhbiByZWFkIGl0IGFzOgoKMS4gU3RhcnQgd2l0aCB0aGUgc3ludGhldGljX2RhdGEgZGF0YWZyYW1lLCBUSEVOCjIuIFB1bGwgb3V0IGp1c3QgdGhlIG1hdGhfc2NvcmUgdmVjdG9yLCBUSEVOCjMuIEV4dHJhY3Qgb25seSB0aGUgZmlyc3QgMyBlbnRyaWVzIChmcm9tIHRoZSBoZWFkIG9yIHRvcCBvZiB0aGUgdmVjdG9yKQoKClF1aXo6IEhvdyB3b3VsZCB5b3UgcmVhZCB0aGUgc2Vjb25kIGV4YW1wbGUgaW4gd29yZHM/CgoKIyMjIFdvcmtpbmcgd2l0aCBEYXRhIEZyYW1lcwoKQSBkYXRhIGZyYW1lIGlzIGEgdGFibGUgd2hlcmUgZWFjaCBjb2x1bW4gaXMgYSB2ZWN0b3IuIExldCdzIGxvb2sgYXQgd2F5cyB0byBleHRyYWN0IHBhcnRzIG9mIG91ciBkYXRhIGZyYW1lOgoKVGhlIGJhc2UgUiB3YXk6CmBgYHtyfQojIEV4dHJhY3Qgb25lIGNvbHVtbiBieSBuYW1lCnN5bnRoZXRpY19kYXRhWywgIm1hdGhfc2NvcmUiXQoKIyBTaG9ydGN1dCEKc3ludGhldGljX2RhdGEkbWF0aF9zY29yZQoKIyBFeHRyYWN0IG11bHRpcGxlIGNvbHVtbnMgYnkgbmFtZQpzeW50aGV0aWNfZGF0YVssIGMoInN0dWRlbnRfaWQiLCAibWF0aF9zY29yZSIpXQoKIyBFeHRyYWN0IHNwZWNpZmljIHJvd3MgKHdoZXJlIG1hdGhfc2NvcmUgPiA4MCkKc3ludGhldGljX2RhdGFbc3ludGhldGljX2RhdGEkbWF0aF9zY29yZSA+IDgwLCBdCmBgYAoKVGhpcyBzeW50YXggY2FuIGJlIGNvbmZ1c2luZyEgTGV0J3MgbG9vayBhdCB0aGUgbW9yZSByZWFkYWJsZSB0aWR5dmVyc2Ugd2F5OgoKYGBge3J9CiMgRXh0cmFjdCBzcGVjaWZpYyBjb2x1bW5zCnN5bnRoZXRpY19kYXRhIHw+CiAgc2VsZWN0KHN0dWRlbnRfaWQsIG1hdGhfc2NvcmUpCgojIEV4dHJhY3Qgc3BlY2lmaWMgcm93cwpzeW50aGV0aWNfZGF0YSB8PgogIGZpbHRlcihtYXRoX3Njb3JlID4gODApCgojIEV4dHJhY3QgYm90aCByb3dzIGFuZCBjb2x1bW5zCnN5bnRoZXRpY19kYXRhIHw+CiAgZmlsdGVyKG1hdGhfc2NvcmUgPiA4MCkgfD4KICBzZWxlY3Qoc3R1ZGVudF9pZCwgbWF0aF9zY29yZSkKYGBgCgpMZXQncyByZWFkIHRoZSBmaXJzdCBleGFtcGxlOgoKMS4gU3RhcnQgd2l0aCB0aGUgc3ludGhldGljX2RhdGEgZGF0YWZyYW1lLCBUSEVOCjIuIFNlbGVjdCBqdXN0IHRoZSBzdHVkZW50X2lkIGFuZCBtYXRoX3Njb3JlIGNvbHVtbnMKClF1aXo6IEhvdyB3b3VsZCB5b3UgcmVhZCB0aGUgc2Vjb25kIGFuZCBsYXN0IGV4YW1wbGVzIHdpdGggYm90aCBmaWx0ZXIgYW5kIHNlbGVjdD8KCgoKIyMgQ29tcHV0aW5nIG5ldyBjb2x1bW5zCgpMZXQncyBsb29rIGF0IHRocmVlIGNvbW1vbiB3YXlzIHRvIGNvbXB1dGUgbmV3IGluZm9ybWF0aW9uIGZyb20gb3VyIGV4aXN0aW5nIGRhdGEuCgojIyMgQ3JlYXRpbmcgY2FsY3VsYXRlZCBjb2x1bW5zIHdpdGggbXV0YXRlKCkKU29tZXRpbWVzIHdlIG5lZWQgdG8gY29tYmluZSBpbmZvcm1hdGlvbiBmcm9tIG11bHRpcGxlIGNvbHVtbnMuIEZvciBleGFtcGxlLCB3ZSBtaWdodCB3YW50IHRvIGNhbGN1bGF0ZSBhbiBhdmVyYWdlIHNjb3JlIGZvciBlYWNoIHN0dWRlbnQ6CgpgYGB7cn0KIyBBZGQgYSBuZXcgY29sdW1uIGZvciBhdmVyYWdlIHNjb3JlCnN5bnRoZXRpY19kYXRhX3dpdGhfYXZlcmFnZSA8LSBzeW50aGV0aWNfZGF0YSB8PgogIG11dGF0ZShhdmVyYWdlX3Njb3JlID0gKG1hdGhfc2NvcmUgKyBlbmdsaXNoX3Njb3JlKSAvIDIpCgpoZWFkKHN5bnRoZXRpY19kYXRhX3dpdGhfYXZlcmFnZSkKYGBgCgpMZXQncyByZWFkIHRoaXMgc3RlcCBieSBzdGVwOgoKMS4gV2Ugc3RhcnQgd2l0aCBzeW50aGV0aWNfZGF0YSwgVEhFTgoyLiBDcmVhdGUgYSBuZXcgY29sdW1uIGNhbGxlZCBhdmVyYWdlX3Njb3JlIGJ5OgogICAtIEFkZGluZyBtYXRoX3Njb3JlIGFuZCBlbmdsaXNoX3Njb3JlIHRvZ2V0aGVyCiAgIC0gRGl2aWRpbmcgYnkgMiB0byBnZXQgdGhlIGF2ZXJhZ2UKMy4gU2F2ZSB0aGUgZW50aXJlIHJlc3VsdCB0byBhIG5ldyBkYXRhIGZyYW1lIGNhbGxlZCBzeW50aGV0aWNfZGF0YV93aXRoX2F2ZXJhZ2UKNC4gUHJpbnQgdGhlIGZpcnN0IGZldyByb3dzIHRvIHZlcmlmeSBpdCB3b3JrZWQKCiMjIyBHcm91cGluZyBkYXRhIGZvciBhbmFseXNpcwpXZSBvZnRlbiB3YW50IHRvIGFuYWx5emUgZGF0YSBieSBjYXRlZ29yaWVzLiBMZXQncyBsb29rIGF0IGhvdyBzY29yZXMgdmFyeSBhY3Jvc3MgZ3JhZGUgbGV2ZWxzOgoKYGBge3J9CiMgQ2FsY3VsYXRlIGF2ZXJhZ2Ugc2NvcmVzIGJ5IGdyYWRlCnN5bnRoZXRpY19kYXRhIHw+CiAgZ3JvdXBfYnkoZ3JhZGUpIHw+CiAgc3VtbWFyaXNlKAogICAgYXZnX21hdGggPSBtZWFuKG1hdGhfc2NvcmUpLAogICAgYXZnX2VuZ2xpc2ggPSBtZWFuKGVuZ2xpc2hfc2NvcmUpCiAgKQpgYGAKClJlYWRpbmcgdGhlIHBpcGU6IFN0YXJ0IHdpdGggc3ludGhldGljX2RhdGEsIFRIRU4gZ3JvdXAgdGhlIHJvd3MgYnkgZ3JhZGUgbGV2ZWwsIFRIRU4gY2FsY3VsYXRlIHN1bW1hcnkgc3RhdGlzdGljcyAoYXZlcmFnZSBtYXRoIGFuZCBlbmdsaXNoIHNjb3JlcykgZm9yIGVhY2ggZ3JhZGUgZ3JvdXAuCgojIyMgQ291bnRpbmcgb2JzZXJ2YXRpb25zIGluIGdyb3VwcwpTb21ldGltZXMgd2UganVzdCBuZWVkIHRvIGtub3cgaG93IG1hbnkgaXRlbXMgZmFsbCBpbnRvIGRpZmZlcmVudCBjYXRlZ29yaWVzOgoKYGBge3J9CiMgQ291bnQgc3R1ZGVudHMgaW4gZWFjaCBncmFkZQpzeW50aGV0aWNfZGF0YSB8PgogIGdyb3VwX2J5KGdyYWRlKSB8PgogIHN1bW1hcmlzZShuX3N0dWRlbnRzID0gbigpKQpgYGAKClJlYWRpbmcgdGhlIHBpcGU6IFN0YXJ0IHdpdGggc3ludGhldGljX2RhdGEsIFRIRU4gZ3JvdXAgdGhlIHJvd3MgYnkgZ3JhZGUgbGV2ZWwsIFRIRU4gY291bnQgaG93IG1hbnkgc3R1ZGVudHMgYXJlIGluIGVhY2ggZ3JhZGUgdXNpbmcgdGhlIG4oKSBmdW5jdGlvbi4KCiMjIERhdGEgQ2xlYW5pbmcKCldoZW4gd2UgbG9hZCBkYXRhIGZyb20gZXh0ZXJuYWwgc291cmNlcywgd2Ugb2Z0ZW4gbmVlZCB0byAiY2xlYW4iIGl0IGJlZm9yZSB3ZSBjYW4gdXNlIGl0IGZvciBhbmFseXNpcy4gQ29tbW9uIGRhdGEgY2xlYW5pbmcgdGFza3MgaW5jbHVkZToKCi0gRml4aW5nIGluY29uc2lzdGVudCBkYXRhIGVudHJ5IChlLmcuLCAiR3JhZGUgOSIgdnMgIjkiIHZzICJOaW5lIikKLSBIYW5kbGluZyBtaXNzaW5nIHZhbHVlcyAoZW1wdHkgY2VsbHMgb3Igc3BlY2lhbCBjb2RlcyBsaWtlIC05OSBuZWVkIHRvIGJlIGNvbnZlcnRlZCB0byBgTkFgKQotIFJlbW92aW5nIGR1cGxpY2F0ZSBlbnRyaWVzCi0gQ29ycmVjdGluZyBkYXRhIHR5cGVzIChlLmcuLCBudW1iZXJzIHN0b3JlZCBhcyB0ZXh0KQotIFN0YW5kYXJkaXppbmcgY29sdW1uIG5hbWVzIGFuZCB2YWx1ZXMKLSBJZGVudGlmeWluZyBhbmQgaGFuZGxpbmcgb3V0bGllcnMKLSBEZWFsaW5nIHdpdGggZXh0cmEgc3BhY2VzIG9yIHNwZWNpYWwgY2hhcmFjdGVycwoKRGF0YSBjbGVhbmluZyBpcyBvZnRlbiB0aGUgbW9zdCB0aW1lLWNvbnN1bWluZyBwYXJ0IG9mIGRhdGEgYW5hbHlzaXMsIGJ1dCBpdCdzIGNydWNpYWwgZm9yIGdldHRpbmcgYWNjdXJhdGUgcmVzdWx0cy4gTGV0J3MgbG9vayBhdCBzb21lIGNvbW1vbiBkYXRhIGNsZWFuaW5nIHRhc2tzIHVzaW5nIG91ciBzdXJ2ZXkgZGF0YToKCiMjIyBIYW5kbGluZyBjb3JyZWN0IGRhdGEgdHlwZXMKCkFib3ZlLCB3ZSBpbXBvcnRlZCBgc3VydmV5X2RhdGExYCBhbmQgYHN1cnZleV9kYXRhMmAuIE9wZW4gdGhvc2UgZGF0YWZyYW1lcyB1c2luZyB0aGUgRW52aXJvbm1lbnQgcGFuZSBhbmQgdGFrZSBhIGxvb2sgYXQgdGhlIHR5cGVzLiBFdmVyeXRoaW5nIHdhcyBpbXBvcnRlZCBhcyBjaGFyYWN0ZXJzISBUaGUgcmVhc29uIGlzIHRoYXQgdGhlIG51bWVyaWNhbCByZXNwb25zZXMgaW5jbHVkZSBzb21lIHRleHQsIGFuZCBSLCB0byBhdm9pZCBkYW1hZ2luZyB0aGUgZGF0YSBieSB0cnlpbmcgdG8gY29udmVydCB0byBudW1iZXJzLCBqdXN0IGltcG9ydGVkIGV2ZXJ5dGhpbmcgYXMgdGV4dC4KCmBgYHtyfQojIFRha2UgYSBsb29rIGF0IG9uZSB2YXJpYWJsZQpzdXJ2ZXlfZGF0YTIkc2Vzc2lvbl9rbm93bGVkZ2UgfD4KICAgIHVuaXF1ZSgpICMgU2hvdyBvbmx5IHRoZSB1bmlxdWUgdmFsdWVzCmBgYAoKV2UgY2FuIHNlZSB0aGUgdmFsdWVzIGFyZSBudW1lcmljLCBmcm9tIGEgTGlrZXJ0LXR5cGUgc2NhbGUuIFdlIHVzdWFsbHkgY29uc2lkZXIgdGhpcyB0eXBlIG9mIGRhdGEgdG8gYmUgb3JkaW5hbCwgcmF0aGVyIHRoYW4gY29udGlub3VzLCBhbmQgaW4gUiwgdGhhdCBpcyBhIHNwZWNpYWwgdmFyaWFibGUgdHlwZSBjYWxsZWQgYW4gb3JkZXJlZCBmYWN0b3IuCgpgYGB7cn0Kc3VydmV5X2RhdGEyIDwtIHN1cnZleV9kYXRhMiB8PgogIG11dGF0ZSgKICAgIHNlc3Npb25fa25vd2xlZGdlID0gZmFjdG9yKAogICAgICBzZXNzaW9uX2tub3dsZWRnZSwKICAgICAgbGV2ZWxzID0gYygiMSAtIE5vdCBhdCBBbGwiLCAiMiIsICIzIiwgIjQiLCAiNSAtIFNpZ25pZmljYW50bHkiKSwKICAgICAgb3JkZXJlZCA9IFRSVUUKICAgICkKICApCgojIENoZWNrIHRoZSByZXN1bHQKc3VydmV5X2RhdGEyJHNlc3Npb25fa25vd2xlZGdlIHw+CiAgbGV2ZWxzKCkKYGBgCk5vdywgd2UgaGF2ZSBrZXB0IHRoZSBjb21iaW5hdGlvbiBvZiB0ZXh0IGFuZCBudW1iZXJzLCBidXQgdGhlIGRhdGEgaXMgbm93IG9yZGVyZWQuIFRoaXMgd2lsbCBiZSB1c2VmdWwgbGF0ZXIgaWYgd2UgdHJ5IHRvIHBsb3QgdGhlIGRhdGEuIEZvciBleGFtcGxlLCBhIGJhciBwbG90IHdvdWxkIGF1dG9tYXRpY2FsbHkgcHV0IHRoZSA1IGxldmVscyBpbiB0aGUgY29ycmVjdCBvcmRlciwgYW5kIHRoZSBoZWxwZnVsIHRleHQgd291bGQgYmUgcGFydCBvZiB0aGUgbGVnZW5kLgoKSWYgd2UgcmVhbGx5IHdhbnRlZCB0aGlzIGRhdGEgdG8gYmUgbnVtZXJpYyAocGVyaGFwcyB0aGUgc2NhbGUgdHJ1bHkgcmVwcmVzZW50cyBhIGNvbnRpbnVvdXMgdmFyaWFibGUpLCB3ZSBjYW4gZXh0cmFjdCBqdXN0IHRoZSBudW1iZXJzIGFuZCBjb252ZXJ0IGl0IHRvIGEgbnVtZXJpYyB2YXJpYWJsZToKCmBgYHtyfQojIEV4dHJhY3QganVzdCB0aGUgZmlyc3QgY2hhcmFjdGVyIGFuZCBjb252ZXJ0IHRvIG51bWVyaWMKc3VydmV5X2RhdGEyIDwtIHN1cnZleV9kYXRhMiB8PgogIG11dGF0ZSgKICAgIHNlc3Npb25fa25vd2xlZGdlX251bSA9IHN1YnN0cihzZXNzaW9uX2tub3dsZWRnZSwgMSwgMSkgfD4KICAgICAgYXMubnVtZXJpYygpCiAgKQoKIyBOb3cgd2UgY2FuIHBlcmZvcm0gY29tcHV0YXRpb25zIGFzIGEgbnVtZXJpY2FsIHZlY3RvcgptZWFuKHN1cnZleV9kYXRhMiRzZXNzaW9uX2tub3dsZWRnZV9udW0sIG5hLnJtID0gVFJVRSkKYGBgCgpOb3RlIHRoYXQgY29udmVydGluZyBvcmRpbmFsIGRhdGEgdG8gbnVtZXJpYyBzaG91bGQgYmUgZG9uZSBjYXJlZnVsbHkgYW5kIHdpdGggcHVycG9zZS4gV2hpbGUgaXQgY2FuIGVuYWJsZSBhZGRpdGlvbmFsIHN0YXRpc3RpY2FsIG9wZXJhdGlvbnMsIGl0IGFzc3VtZXMgdGhlIGRpc3RhbmNlcyBiZXR3ZWVuIGxldmVscyBhcmUgZXF1YWwgYW5kIG1lYW5pbmdmdWwuIFdlIGhhdmUga2VwdCB0aGUgb3JpZ2lvbmFsIG9yZGVyZWQgZmFjdG9yIGFuZCBjcmVhdGVkIGEgc2VwZXJhdGUgbnVtZXJpYyB2ZXJzaW9uLiBFdmVuIGlmIHRoZSBudW1lcmljIHZlcnNpb24gaXMgaW5hcHByb3ByaWF0ZSB0byB0aGUgY2F0ZWdvcmljYWwgTGlrZXJ0LXR5cGUgcmVzcG9uc2UsIGl0IG1heSBzdGlsbCBiZSB1c2VmdWwgZm9yIGZpbmRpbmcgb3V0bGllcnMuIFdlIGNvdWxkIGNyZWF0ZSBudW1lcmljYWwgdmVyc2lvbnMgb2YgYWxsIHRoZSBpdGVtcywgYW5kIGFkZCB0aGUgY29sdW1ucyB0byBpZGVudGlmeSBleHRyZW1lcy4KCiMjIyBDb3JyZWN0aW5nIGluY29uc2lzdGVudCBkYXRhIGVudHJ5IGFuZCBtaXNzaW5nIHZhbHVlcwoKTGV0J3MgY3JlYXRlIGEgc21hbGwgZGF0YXNldCB3aXRoIHNvbWUgY29tbW9uIGRhdGEgcHJvYmxlbXM6CgpgYGB7cn0KIyBDcmVhdGUgbWVzc3kgZGF0YQptZXNzeV9kYXRhIDwtIGRhdGEuZnJhbWUoCiAgc3R1ZGVudF9pZCA9IDE6NSwKICBncmFkZSA9IGMoIkdyYWRlIDkiLCAiOSIsICJncmFkZSA5IiwgIkdyYWRlIDkiLCAiLTk5IiksCiAgc2NvcmUgPSBjKDg1LCA5MiwgNzgsIC05OSwgODgpCikKCiMgVmlldyB0aGUgbWVzc3kgZGF0YQptZXNzeV9kYXRhCgojIFN0ZXAgMTogRml4IGluY29uc2lzdGVudCBncmFkZSBlbnRyaWVzIHRoYXQgc3RhcnQgd2l0aCAiR3JhZGUiCmNsZWFuX2RhdGEgPC0gbWVzc3lfZGF0YSB8PgogIG11dGF0ZSgKICAgIGdyYWRlID0gc3RyX3JlcGxhY2UoZ3JhZGUsICJHcmFkZSA5IiwgIjkiKQogICkKCiMgU3RlcCAyOiBGaXggZ3JhZGVzIHRoYXQgc3RhcnQgd2l0aCBsb3dlcmNhc2UgImdyYWRlIgpjbGVhbl9kYXRhIDwtIGNsZWFuX2RhdGEgfD4KICBtdXRhdGUoCiAgICBncmFkZSA9IHN0cl9yZXBsYWNlKGdyYWRlLCAiZ3JhZGUgOSIsICI5IikKICApCgojIFN0ZXAgMzogUmVwbGFjZSAtOTkgd2l0aCBOQSBpbiBib3RoIGdyYWRlIGFuZCBzY29yZQpjbGVhbl9kYXRhIDwtIGNsZWFuX2RhdGEgfD4KICBtdXRhdGUoCiAgICBncmFkZSA9IGlmX2Vsc2UoZ3JhZGUgPT0gIi05OSIsIE5BX2NoYXJhY3Rlcl8sIGdyYWRlKSwKICAgIHNjb3JlID0gaWZfZWxzZShzY29yZSA9PSAtOTksIE5BX3JlYWxfLCBzY29yZSkKICApCgojIFZpZXcgdGhlIGNsZWFuZWQgZGF0YQpjbGVhbl9kYXRhCmBgYAoKSW4gdGhpcyBleGFtcGxlLCB3ZSB1c2VkOgoKLSBgc3RyX3JlcGxhY2UoKWAgdG8gaGFuZGxlIHRleHQgcmVwbGFjZW1lbnRzIGluIHRoZSBncmFkZSBjb2x1bW4KLSBgaWZfZWxzZSgpYCB0byByZXBsYWNlIHNwZWNpZmljIHZhbHVlcyB3aXRoIE5BIGluIHRoZSBzY29yZSBjb2x1bW4KLSBgTkFfY2hhcmFjdGVyX2AgYW5kIGBOQV9yZWFsX2AgdG8gc3BlY2lmeSB0aGUgdHlwZSBvZiBOQSB2YWx1ZQoKCgojIyMgUmVtb3ZpbmcgZHVwbGljYXRlcwoKTGV0J3MgY3JlYXRlIGEgc21hbGwgZGF0YXNldCB3aXRoIHNvbWUgZHVwbGljYXRlIGVudHJpZXM6CgpgYGB7cn0KIyBDcmVhdGUgZGF0YSB3aXRoIGR1cGxpY2F0ZXMKZHVwbGljYXRlX2RhdGEgPC0gdGliYmxlKAogIFJlc3BvbnNlSUQgPSBjKDEwMSwgMTAxLCAxMDIsIDEwMywgMTAzKSwKICBkYXRlID0gYygiMjAyMy0wMS0xNSIsICIyMDIzLTAxLTE1IiwgIjIwMjMtMDItMDEiLCAiMjAyMy0wMS0wMSIsICIyMDIzLTAyLTAxIiksCiAgc2NvcmUgPSBjKDg1LCA4NSwgOTIsIDc4LCA4MikKKQoKIyBWaWV3IHRoZSBkYXRhCmR1cGxpY2F0ZV9kYXRhCgojIFJlbW92ZSBleGFjdCBkdXBsaWNhdGVzIChFbnRpcmUgZW50cnkgaXMgYSBkdXBsaWNhdGUpCnVuaXF1ZV9kYXRhIDwtIGR1cGxpY2F0ZV9kYXRhIHw+CiAgZGlzdGluY3QoKQoKIyBWaWV3IHJlc3VsdCBhZnRlciByZW1vdmluZyBleGFjdCBkdXBsaWNhdGVzCnVuaXF1ZV9kYXRhCgojIEtlZXAgb25seSBtb3N0IHJlY2VudCBlbnRyeSBmb3IgZWFjaCBSZXNwb25zZUlEIChlLmcuLCBzb21lb25lIHBhcnRpY2lwYXRlZCB0d2ljZSkKbGF0ZXN0X2RhdGEgPC0gZHVwbGljYXRlX2RhdGEgfD4KICBncm91cF9ieShSZXNwb25zZUlEKSB8PgogIHNsaWNlX21heChkYXRlKSB8PgogIHVuZ3JvdXAoKQoKIyBWaWV3IGZpbmFsIHJlc3VsdApsYXRlc3RfZGF0YQpgYGAKCkluIHRoaXMgZXhhbXBsZSwgd2UgZmlyc3QgcmVtb3ZlIGV4YWN0IGR1cGxpY2F0ZXMgd2l0aCBgZGlzdGluY3QoKWAsIHRoZW4gdXNlIGBzbGljZV9tYXgoKWAgdG8ga2VlcCBvbmx5IHRoZSBtb3N0IHJlY2VudCBlbnRyeSBmb3IgZWFjaCBSZXNwb25zZUlELiBUaGlzIGlzIGEgY29tbW9uIHRhc2sgd2hlbiBkZWFsaW5nIHdpdGggbXVsdGlwbGUgcmVzcG9uc2VzIGZyb20gdGhlIHNhbWUgcGFydGljaXBhbnQuCgoKIyMjIElkZW50aWZ5aW5nIE91dGxpZXJzCgpMZXQncyBhZGQgc29tZSBvdXRsaWVycyB0byBvdXIgc3ludGhldGljIGRhdGEgYW5kIHRoZW4gaWRlbnRpZnkgdGhlbToKCmBgYHtyfQojIEFkZCBzb21lIG91dGxpZXIgbWF0aCBzY29yZXMKc3ludGhldGljX2RhdGEkbWF0aF9zY29yZVsxOjNdIDwtIGMoMTUsIDIwLCAxMjUpICAjIEFkZCB1bnJlYWxpc3RpYyBzY29yZXMKCiMgQ2FsY3VsYXRlIHN1bW1hcnkgc3RhdGlzdGljcwpvdXRsaWVyX2FuYWx5c2lzIDwtIHN5bnRoZXRpY19kYXRhIHw+CiAgc3VtbWFyaXNlKAogICAgcTEgPSBxdWFudGlsZShtYXRoX3Njb3JlLCAwLjI1KSwKICAgIHEzID0gcXVhbnRpbGUobWF0aF9zY29yZSwgMC43NSksCiAgICBpcXIgPSBxMyAtIHExLAogICAgbG93ZXJfYm91bmQgPSBxMSAtIDEuNSAqIGlxciwKICAgIHVwcGVyX2JvdW5kID0gcTMgKyAxLjUgKiBpcXIKICApCgojIEZpbmQgb3V0bGllcnMKb3V0bGllcnMgPC0gc3ludGhldGljX2RhdGEgfD4KICBmaWx0ZXIoCiAgICBtYXRoX3Njb3JlIDwgb3V0bGllcl9hbmFseXNpcyRsb3dlcl9ib3VuZCB8CiAgICBtYXRoX3Njb3JlID4gb3V0bGllcl9hbmFseXNpcyR1cHBlcl9ib3VuZAogICkKCiMgVmlldyB0aGUgb3V0bGllcnMKb3V0bGllcnMgfD4KICBzZWxlY3Qoc3R1ZGVudF9pZCwgbWF0aF9zY29yZSkKCiMgUmVtb3ZlIG91dGxpZXJzIGZyb20gb3VyIGRhdGEKc3ludGhldGljX2RhdGFfY2xlYW4gPC0gc3ludGhldGljX2RhdGEgfD4KICBmaWx0ZXIoCiAgICBtYXRoX3Njb3JlID4gb3V0bGllcl9hbmFseXNpcyRsb3dlcl9ib3VuZCwKICAgIG1hdGhfc2NvcmUgPCBvdXRsaWVyX2FuYWx5c2lzJHVwcGVyX2JvdW5kCiAgKQoKIyBWZXJpZnkgdGhlIG91dGxpZXJzIGFyZSBnb25lCnN1bW1hcnkoc3ludGhldGljX2RhdGFfY2xlYW4kbWF0aF9zY29yZSkKYGBgCgpUaGlzIGV4YW1wbGUgdXNlcyB0aGUgSW50ZXJxdWFydGlsZSBSYW5nZSAoSVFSKSBtZXRob2QgdG8gaWRlbnRpZnkgb3V0bGllcnMuIFZhbHVlcyB0aGF0IGZhbGwgbW9yZSB0aGFuIDEuNSB0aW1lcyB0aGUgSVFSIGJlbG93IHRoZSBmaXJzdCBxdWFydGlsZSBvciBhYm92ZSB0aGUgdGhpcmQgcXVhcnRpbGUgYXJlIGNvbnNpZGVyZWQgb3V0bGllcnMuIFdoZW4gZGVhbGluZyB3aXRoIHJlYWwgZGF0YSwgeW91IHNob3VsZCBpbnZlc3RpZ2F0ZSBvdXRsaWVycyBjYXJlZnVsbHkgLSB0aGV5IG1pZ2h0IHJlcHJlc2VudCBkYXRhIGVudHJ5IGVycm9ycywgb3IgdGhleSBtaWdodCBiZSB2YWxpZCBidXQgdW51c3VhbCBjYXNlcy4KCgoKCgojIyBKb2luaW5nIERhdGEKClNvbWV0aW1lcyB3ZSBuZWVkIHRvIGNvbWJpbmUgdHdvIGRpZmZlcmVudCBkYXRhc2V0cy4gRm9yIGV4YW1wbGUsIHlvdSBtaWdodCBoYXZlOgotIFN0dWRlbnQgZGVtb2dyYXBoaWMgaW5mb3JtYXRpb24gaW4gb25lIHNwcmVhZHNoZWV0Ci0gVGVzdCBzY29yZXMgaW4gYW5vdGhlciBzcHJlYWRzaGVldAotIFN1cnZleSByZXNwb25zZXMgaW4geWV0IGFub3RoZXIKCkluIEV4Y2VsLCB5b3UgbWlnaHQgdXNlIFZMT09LVVAgdG8gbWF0Y2ggaW5mb3JtYXRpb24gYmV0d2VlbiBzcHJlYWRzaGVldHMuIEluIFIgYW5kIG1vc3QgRGF0YWJhc2Ugc3lzdGVtcywgd2UgdXNlICJqb2lucyIgdG8gY29tYmluZSBkYXRhc2V0cyBiYXNlZCBvbiBhIHNoYXJlZCBpZGVudGlmaWVyIGNvbHVtbiwgbGlrZSBzdHVkZW50IG51bWJlci4gVGhpcyBpcyBhbHNvIGhvdyBNaWNyb3NvZnQgQWNjZXNzIGFuZCBQb3dlckJJIGNvbWJpbmUgdGFibGVzIGFzIHdlbGwuCgpMZXQncyBsb29rIGF0IG91ciBzdXJ2ZXkgZGF0YToKYGBge3J9CiMgTG9vayBhdCB0aGUgZmlyc3QgZmV3IHJvd3Mgb2YgZWFjaCBkYXRhc2V0CmhlYWQoc3VydmV5X2RhdGExKQpoZWFkKHN1cnZleV9kYXRhMikKYGBgCgpCb3RoIGRhdGFzZXRzIGhhdmUgYSBSZXNwb25kZW50SUQgY29sdW1uIHRoYXQgd2UgY2FuIHVzZSB0byBtYXRjaCByb3dzIGJldHdlZW4gdGhlIGRhdGFzZXRzLiBIb3dldmVyLCBub3QgZXZlcnkgcmVzcG9uZGVudCBhcHBlYXJzIGluIGJvdGggZGF0YXNldHM6CgpgYGB7cn0KIyBXaGljaCByZXNwb25kZW50cyBhcmUgb25seSBpbiBkYXRhc2V0IDE/CnN1cnZleV9kYXRhMSB8PgogIGZpbHRlcighUmVzcG9uZGVudElEICVpbiUgc3VydmV5X2RhdGEyJFJlc3BvbmRlbnRJRCkgfD4KICBwdWxsKFJlc3BvbmRlbnRJRCkgfD4KICBsZW5ndGgoKQoKIyBXaGljaCByZXNwb25kZW50cyBhcmUgb25seSBpbiBkYXRhc2V0IDI/CnN1cnZleV9kYXRhMiB8PgogIGZpbHRlcighUmVzcG9uZGVudElEICVpbiUgc3VydmV5X2RhdGExJFJlc3BvbmRlbnRJRCkgfD4KICBwdWxsKFJlc3BvbmRlbnRJRCkgfD4KICBsZW5ndGgoKQpgYGAKClRoZXJlIGFyZSBmb3VyIG1haW4gd2F5cyB0byBqb2luIGRhdGFzZXRzOgoKMS4gSW5uZXIgam9pbjogS2VlcCBvbmx5IHJvd3MgdGhhdCBtYXRjaCBpbiBib3RoIGRhdGFzZXRzCjIuIExlZnQgam9pbjogS2VlcCBhbGwgcm93cyBmcm9tIHRoZSBmaXJzdCBkYXRhc2V0LCBhZGQgbWF0Y2hpbmcgZGF0YSBmcm9tIHRoZSBzZWNvbmQKMy4gUmlnaHQgam9pbjogS2VlcCBhbGwgcm93cyBmcm9tIHRoZSBzZWNvbmQgZGF0YXNldCwgYWRkIG1hdGNoaW5nIGRhdGEgZnJvbSB0aGUgZmlyc3QKNC4gRnVsbCBqb2luOiBLZWVwIGFsbCByb3dzIGZyb20gYm90aCBkYXRhc2V0cwoKCgpgYGB7cn0KIyBJbm5lciBqb2luIC0gb25seSBtYXRjaGluZyByb3dzCmlubmVyX2pvaW5lZCA8LSBzdXJ2ZXlfZGF0YTEgfD4KICBpbm5lcl9qb2luKHN1cnZleV9kYXRhMiwgYnkgPSAiUmVzcG9uZGVudElEIikKCiMgTGVmdCBqb2luIC0gYWxsIHJvd3MgZnJvbSBzdXJ2ZXlfZGF0YTEKbGVmdF9qb2luZWQgPC0gc3VydmV5X2RhdGExIHw+CiAgbGVmdF9qb2luKHN1cnZleV9kYXRhMiwgYnkgPSAiUmVzcG9uZGVudElEIikKCiMgQ29tcGFyZSB0aGUgbnVtYmVyIG9mIHJvd3MKbnJvdyhpbm5lcl9qb2luZWQpICAjIEZld2VyIHJvd3MgLSBvbmx5IG1hdGNoaW5nIFJlc3BvbmRlbnRJRHMKbnJvdyhsZWZ0X2pvaW5lZCkgICAjIFNhbWUgbnVtYmVyIG9mIHJvd3MgYXMgc3VydmV5X2RhdGExCmBgYAoKVXN1YWxseSwgeW91J2xsIHVzZSBsZWZ0X2pvaW4oKSB0byBhZGQgaW5mb3JtYXRpb24gdG8geW91ciBtYWluIGRhdGFzZXQuIEFueSByZXNwb25kZW50cyB0aGF0IGRvbid0IGhhdmUgbWF0Y2hpbmcgZGF0YSB3aWxsIGdldCBOQSB2YWx1ZXMgaW4gdGhlIGNvbHVtbnMgZnJvbSB0aGUgc2Vjb25kIGRhdGFzZXQuIFRha2UgYSBsb29rIGF0IGBsZWZ0X2pvaW5lZGAgdG8gc2VlIHdoYXQgSSBtZWFuLgoKCiMjIEVuZCBvZiBNb2R1bGUgMwpJbiB0aGlzIG1vZHVsZSwgd2UndmUgY292ZXJlZCBhIGxvdCBvZiBncm91bmQhIFlvdSBub3cgdW5kZXJzdGFuZCBob3cgdG86CgotIFdvcmsgd2l0aCBidWlsdC1pbiBkYXRhc2V0cyBhbmQgY3JlYXRlIHN5bnRoZXRpYyBkYXRhIGZvciB0ZXN0aW5nCi0gSGFuZGxlIG1pc3NpbmcgZGF0YSB3aXRoIE5BIHZhbHVlcwotIExvYWQgZGF0YSBmcm9tIGZpbGVzIGFuZCBvbmxpbmUgc291cmNlcwotIFZpZXcgYW5kIGV4cGxvcmUgZGF0YSBpbiBSU3R1ZGlvCi0gRXh0cmFjdCBzcGVjaWZpYyBwYXJ0cyBvZiB5b3VyIGRhdGEgdXNpbmcgYm90aCBiYXNlIFIgYW5kIHRpZHl2ZXJzZSBhcHByb2FjaGVzCi0gQ2xlYW4gbWVzc3kgZGF0YSBhbmQgaGFuZGxlIGNvbW1vbiBkYXRhIHByb2JsZW1zCi0gSm9pbiBkYXRhc2V0cyB0b2dldGhlciB1c2luZyBkaWZmZXJlbnQgdHlwZXMgb2Ygam9pbnMKCkNvbmdyYXR1bGF0aW9ucyBvbiBtYWtpbmcgaXQgdGhyb3VnaCB0aGUgbW9zdCBjaGFsbGVuZ2luZyBwYXJ0ISBEYXRhIGNsZWFuaW5nIGFuZCBwcmVwYXJhdGlvbiB0eXBpY2FsbHkgdGFrZXMgdXAgbW9zdCBvZiBvdXIgdGltZSBpbiByZWFsLXdvcmxkIGFuYWx5c2lzLCBidXQgaGF2aW5nIHRoZXNlIHNraWxscyBtZWFucyB5b3UgY2FuIHdvcmsgd2l0aCBhbG1vc3QgYW55IGRhdGFzZXQgaW4gUiBhbmQgUlN0dWRpby4KCkluIHRoZSBuZXh0IG1vZHVsZSwgd2UnbGwgZm9jdXMgb24gc29tZXRoaW5nIG1vcmUgcmV3YXJkaW5nIC0gY3JlYXRpbmcgaGlnaC1xdWFsaXR5IGZpZ3VyZXMgYW5kIHJlcG9ydHMgdXNpbmcgUiBNYXJrZG93bi4gWW91J2xsIGxlYXJuIGhvdyB0byBwcmVzZW50IHlvdXIgYW5hbHlzaXMgcHJvZmVzc2lvbmFsbHkgYW5kIGNyZWF0ZSByZXByb2R1Y2libGUgZG9jdW1lbnRzIHRoYXQgY29tYmluZSB0ZXh0LCBjb2RlLCBhbmQgdmlzdWFsaXphdGlvbnMuCg==