knitr::opts_chunk$set(
 echo = TRUE,
 message = FALSE,
 warning = FALSE
)
load(".RData")

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)
NA

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:

  1. 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.

  2. 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.

Extracting Data

Working with Vectors

Remember that a vector is just a single list of one type of data (like numbers or text). Let’s look at the math_score vector:

The base R way:

# Extract the first value in math_score
synthetic_data$math_score[1]
[1] 85
# Extract the first three values of math_score
synthetic_data$math_score[1:3]
[1] 85 67 60
# Extract all math scores over 80
synthetic_data$math_score[synthetic_data$math_score > 80]
 [1]  85  81  88  88  88 101  83  83  87  84  83  86  82  89  90  82  81

The tidyverse way:

synthetic_data |>
  pull(math_score) |>
  head(3)
[1] 85 67 60
synthetic_data |>
  filter(math_score > 80) |>
  pull(math_score)
 [1]  85  81  88  88  88 101  83  83  87  84  83  86  82  89  90  82  81

Always remember to read the pipe symbol |> as “THEN.” In the first example, you can read it as:

  1. Start with the synthetic_data dataframe, THEN
  2. Pull out just the math_score vector, THEN
  3. Extract only the first 3 entries (from the head or top of the vector)

Quiz: How would you read the second example in words?

Working with Data Frames

A data frame is a table where each column is a vector. Let’s look at ways to extract parts of our data frame:

The base R way:

# Extract one column by name
synthetic_data[, "math_score"]
 [1]  85  67  60  81  66  61  64  66  76  88  76  88  88 101  70  83  71
[18]  83  87  79  84  83  77  72  73  76  86  69  57  82  80  77  79  61
[35]  89  90  73  82  64  70  69  65  77  79  64  63  76  80  81  74
# Shortcut!
synthetic_data$math_score
 [1]  85  67  60  81  66  61  64  66  76  88  76  88  88 101  70  83  71
[18]  83  87  79  84  83  77  72  73  76  86  69  57  82  80  77  79  61
[35]  89  90  73  82  64  70  69  65  77  79  64  63  76  80  81  74
# Extract multiple columns by name
synthetic_data[, c("student_id", "math_score")]

# Extract specific rows (where math_score > 80)
synthetic_data[synthetic_data$math_score > 80, ]

This syntax can be confusing! Let’s look at the more readable tidyverse way:

# Extract specific columns
synthetic_data |>
  select(student_id, math_score)

# Extract specific rows
synthetic_data |>
  filter(math_score > 80)

# Extract both rows and columns
synthetic_data |>
  filter(math_score > 80) |>
  select(student_id, math_score)

Let’s read the first example:

  1. Start with the synthetic_data dataframe, THEN
  2. Select just the student_id and math_score columns

Quiz: How would you read the second and last examples with both filter and select?

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:

  1. We start with synthetic_data, THEN
  2. Create a new column called average_score by:
    • Adding math_score and english_score together
    • Dividing by 2 to get the average
  3. Save the entire result to a new data frame called synthetic_data_with_average
  4. 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)
head(survey_data2)

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:

  1. Inner join: Keep only rows that match in both datasets
  2. Left join: Keep all rows from the first dataset, add matching data from the second
  3. Right join: Keep all rows from the second dataset, add matching data from the first
  4. 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==