# load libraries
library(readr)
library(dplyr)

1.Introduction

A huge part of data science involves acquiring raw data and getting it into a form ready for analysis. Some have estimated that data scientists spend 80% of their time cleaning and manipulating data, and only 20% of their time actually analyzing it or building models from it.

When we receive raw data, we have to do a number of things before we’re ready to analyze it, possibly including:

1.diagnosing the “tidiness” of the data — how much data cleaning we will have to do

2.reshaping the data — getting the right rows and columns for effective analysis

3.combining multiple files

4.changing the types of values — how we fix a column where numerical values are stored as Preview: Docs Loading link description strings, for example

5.dropping or filling missing values - how we deal with data that is incomplete or missing

6.manipulating strings to represent the data better

We will go through the techniques data scientists use to accomplish these goals by looking at some “unclean” datasets and trying to get them into a good, clean state. Along the way we will use the powerful tidyverse packages dplyr and tidyr to get our data squeaky clean!

Instuctions

We have provided an example of data representing exam scores from 1000 students in an online math class.

These data frames, which you can view in the rendered notebook, are hard to work with. They’re separated into multiple tables, and the values don’t lend themselves well to analysis. Try to think about how you would plot the exam score average against the age of the students in the class. This would not be easy!

In the next exercises, we’ll transform this data so that performing a task like that visualization would be simple.

# load data frame
students_1 <- read_csv('exams_0.csv')
students_2 <- read_csv('exams_1.csv')
# inspect data frame
head(students_1)
head(students_2)

2.Diagnose the Data

We often describe data that is easy to analyze and visualize as “tidy data”. What does it mean to have tidy data?

For data to be tidy, it must have:

1.Each variable as a separate column

2.Each row as a separate observation

The first step of diagnosing whether or not a dataset is tidy is using base R and dplyr functions to explore and probe the dataset.

You’ve seen most of the functions we often use to diagnose a dataset for cleaning. Some of the most useful ones are:

1.head() — display the first 6 rows of the table

2.summary() — display the summary statistics of the table

3.colnames() — display the column names of the table

# read CSVs
grocery_1 <- read_csv('grocery_1.csv')
grocery_2 <- read_csv('grocery_2.csv')

Instructions

1.Provided in notebook.Rmd are two data frames, grocery_1 and grocery_2.

Begin by viewing the head() of both grocery_1 and grocery_2.

# inspect data frames
head(grocery_1)
head(grocery_2)
summary(grocery_1)
 Grocery Item        Cake Recipe    Pancake Recipe Cookie Recipe  
 Length:3           Min.   :1.000   Min.   :1.0    Min.   :1.000  
 Class :character   1st Qu.:1.500   1st Qu.:1.5    1st Qu.:1.000  
 Mode  :character   Median :2.000   Median :2.0    Median :1.000  
                    Mean   :1.667   Mean   :2.0    Mean   :1.333  
                    3rd Qu.:2.000   3rd Qu.:2.5    3rd Qu.:1.500  
                    Max.   :2.000   Max.   :3.0    Max.   :2.000  
summary(grocery_2)
 Grocery Item          Recipe              Number     
 Length:9           Length:9           Min.   :1.000  
 Class :character   Class :character   1st Qu.:1.000  
 Mode  :character   Mode  :character   Median :2.000  
                                       Mean   :1.667  
                                       3rd Qu.:2.000  
                                       Max.   :3.000  
colnames(grocery_1)
[1] "Grocery Item"   "Cake Recipe"    "Pancake Recipe" "Cookie Recipe" 
colnames(grocery_2)
[1] "Grocery Item" "Recipe"       "Number"      

2.Explore the data frames using the other functions listed.

Which data frame is “clean”, tidy, and ready for analysis? Create a variable named clean_data_frame and assign it the value 1 if grocery_1 is a clean and tidy data frame or 2 if grocery_2 is a clean and tidy data frame.

# clean data frame
clean_data_frame <- 2

3.Dealing with Multiple Files

Often, you have the same data separated out into multiple files.

Let’s say that you have a ton of files following the filename structure: ‘file_1.csv’, ‘file_2.csv’, ‘file_3.csv’, and so on. The power of dplyr and tidyr is mainly in being able to manipulate large amounts of structured data, so you want to be able to get all of the relevant information into one table so that you can analyze the aggregate data.

You can combine the base R functions list.files() and lapply() with readr and dplyr to organize this data better, as shown below: ” files <- list.files(pattern = “file_.*csv”) df_list <- lapply(files,read_csv) df <- bind_rows(df_list) ” 1.The first line uses list.files() and a regular expression, a sequence of characters describing a pattern of text that should be matched, to find any file in the current directory that starts with ‘file_’ and has an extension of csv, storing the name of each file in a vector files

2.The second line uses lapply() to read each file in files into a data frame with read_csv(), storing the data frames in df_list

3.The third line then concatenates all of those data frames together with dplyr’s bind_rows() function

Instructions

1.You have 10 different files containing 100 students each. These files follow the naming structure:

exams_0.csv exams_1.csv … up to exams_9.csv

You are going to read each file into an individual data frame and then combine all of the entries into one data frame.

First, create a variable called student_files and set it equal to the list.files() of all of the CSV files we want to import.

# list files
student_files <- list.files(pattern = "exams_.*csv")
student_files
 [1] "exams_0.csv" "exams_1.csv" "exams_2.csv" "exams_3.csv" "exams_4.csv" "exams_5.csv"
 [7] "exams_6.csv" "exams_7.csv" "exams_8.csv" "exams_9.csv"

2.Read each file in student_files into a data frame using lapply() and save the result to df_list.

# read files
df_list <- lapply(student_files, read_csv)
df_list
[[1]]

[[2]]

[[3]]

[[4]]

[[5]]

[[6]]

[[7]]

[[8]]

[[9]]

[[10]]
NA

3.Concatenate all of the data frames in df_list into one data frame called students.

# concatenate data frames
students <- bind_rows(df_list)
students

4.Inspect students. Save the number of rows in students to nrow_students. Did you get all of them

# number of rows in students
nrow_students <- nrow(students)
print(nrow_students)
[1] 1000

4.Reshaping your Data

Since we want

1.Each variable as a separate column

2.Each row as a separate observation

We would want to reshape a table like:

Account Checking Savings

“12456543” 8500 8900

“12283942” 6410 8020

“12839485” 78000 92000

Into a table that looks more like:

Account Account Type Amount

“12456543” “Checking” 8500

“12456543” “Savings” 8900

“12283942” “Checking” 6410

“12283942” “Savings” 8020

“12839485” “Checking” 78000

“12839485” “Savings” 920000

We can use tidyr’s gather() function to do this transformation. gather() takes a data frame and the columns to unpack:

library(tidyr)

” df %>% gather(‘Checking’,‘Savings’,key=‘Account Type’,value=‘Amount’) ” The arguments you provide are:

1.df: the data frame you want to gather, which can be piped into gather()

2.Checking and Savings: the columns of the old data frame that you want to turn into variables

3.key: what to call the column of the new data frame that stores the variables

4.value: what to call the column of the new data frame that stores the values

Instructions

1.The students data frame from the previous exercise has been loaded into the notebook for you. Save the columns names to original_col_names and print it.

# original column names
original_col_names <- colnames(students)
original_col_names
[1] "id"          "full_name"   "gender_age"  "fractions"   "probability" "grade"      

2.There is a column for the scores on the fractions exam, and a column for the scores on the probability exam.

We want to make each row an observation, so we want to transform this table to look like:

full_name exam score gender_age grade

“First Student” “fractions” score% … …

“First Student” “probability” score% … …

“Second Student” “fractions” score% … …

“Second Student” “probability” score% … …

Use gather to create a new table (still called students) that follows this structure. Then view the head() of students.

# gather columns
students <- students %>%
  gather('fractions', 'probability', key = 'exam', value = 'score')
head(students)

3.Save the columns names of the updated students data frame to gathered_col_names and print it.

# updated column names
gathered_col_names <- colnames(students)
gathered_col_names
[1] "id"         "full_name"  "gender_age" "grade"      "exam"       "score"     

4.The dplyr function count() takes a data frame and a column as arguments and returns a table with counts of the unique values in the named column.

Find the count of each unique value in the exam column. Save the result to exam_counts and view exam_counts.

# unique value counts of exam
exam_counts <- students %>%
  count(exam)
exam_counts

5.Dealing with Duplicates

Often we see duplicated rows of data in the data frames we are working with. This could happen due to errors in data collection or in saving and loading the data.

To check for duplicates, we can use the base R function duplicated(), which will return a logical vector telling us which rows are duplicate rows.

Let’s say we have a data frame fruits that represents this table:

item price calories

“banana” “$1” 105

“apple” “$0.75” 95

“apple” “$0.75” 95

“peach” “$3” 55

“peach” “$4” 55

“clementine” “$2.5” 35

If we call fruits %>% duplicated(), we would get the following vector:

“>> [1] FALSE FALSE TRUE FALSE FALSE FALSE”

We can see that the third row, which represents an “apple” with price “$0.75” and 95 calories, is a duplicate row. Every value in this row is the same as in another row (the previous row).

We can use the dplyr distinct() function to remove all rows of a data frame that are duplicates of another row.

If we call fruits %>% distinct(), we would get the table:

item price calories

“banana” “$1” 105

“apple” “$0.75” 95

“peach” “$3” 55

“peach” “$4” 55

“clementine” “$2.5” 35

The “apple” row was deleted because it was exactly the same as another row. But the two “peach” rows remain because there is a difference in the price column.

If we wanted to remove every row with a duplicate value in the item column, we could specify a subset:

“fruits %>% distinct(item,.keep_all=TRUE)”

By default, this keeps the first occurrence of the duplicate:

item price calories

“banana” “$1” 105

“apple” “$0.75” 95

“peach” “$3” 55

“clementine” “$2.5” 35

Make sure that the columns you drop duplicates from are specifically the ones where duplicates don’t belong. You wouldn’t want to drop duplicates with the price column as a subset, for example, because it’s okay if multiple items cost the same amount!

Instructions

1.The students data frame has a column id that is neither unique nor required for our analysis. Drop the id column from the data frame and save the result to students. View the head() of students.

# drop id column
students <- students %>%
  select(-id)
head(students)
  1. It seems like in the data collection process, some rows may have been recorded twice. Use the duplicated() function on the students data frame to make a vector object called duplicates.
# find duplicated rows
duplicates <- students %>%
  duplicated()
head(duplicates)
[1] FALSE FALSE FALSE FALSE FALSE FALSE

3.table() is a base R function that takes any R object as an argument and returns a table with the counts of each unique value in the object.

Pipe the result from the previous checkpoint into table() to see how many rows are exact duplicates. Make sure to save the result to duplicate_counts, and view duplicate_counts.

# count duplicated rows
duplicate_counts <- duplicates %>%
  table()
duplicate_counts
.
FALSE  TRUE 
 1976    24 

4.Get rid of the duplicate rows in the students data frame and save this new data frame as unique_students.

# remove duplicated rows, keep only unique rows
unique_students <- students %>%
  distinct()
unique_students

5.Use the duplicated() function again to make an object called updated_duplicates after dropping the duplicates. Pipe the result into table() to see if any duplicates remain, and view updated_duplicates. Are there any TRUEs left?

# find and count duplicated rows in updated data frame
updated_duplicates <- unique_students %>%
  duplicated() %>%
  table()
updated_duplicates
.
FALSE 
 1976 

6.Splitting By Index

In trying to get clean data, we want to make sure each column represents one type of measurement. Often, multiple measurements are recorded in the same column, and we want to separate these out so that we can do individual analysis on each variable.

Let’s say we have a column “birthday” with data formatted in MMDDYYYY format. In other words, “11011993” represents a birthday of November 1, 1993. We want to split this data into day, month, and year so that we can use these columns as separate features.

In this case, we know the exact structure of these strings. The first two characters will always correspond to the month, the second two to the day, and the rest of the string will always correspond to year. We can easily break the data into three separate columns by splitting the strings into substrings using str_sub(), a helpful function from the stringr package:

// Create the ‘month’ column df %>% mutate(month = str_sub(birthday,1,2))

// Create the ‘day’ column df %>% mutate(day = str_sub(birthday,3,4))

// Create the ‘year’ column df %>% mutate(year = str_sub(birthday,5))

1.The first command takes the characters starting at index 1 and ending at index 2 of each value in the birthday column and puts it into a month column.

2.The second command takes the characters starting at index 3 and ending at index 4 of each value in the birthday column and puts it into a day column.

3.The third command takes the characters starting at index 5 and ending at the end of the value in the birthday column and puts it into a year column.

This would transform a table like:

id birthday

1011 “12241989”

1112 “10311966”

1113 “01052011”

into a table like:

id birthday month day year

1011 “12241989” “12” “24” “1989”

1112 “10311966” “10” “31” “1966”

1113 “01052011” “01” “05” “2011”

We will practice changing string columns into numerical columns (like converting “10” to 10) in a future exercise.

Instructions

1.Print out the columns of the students data frame.

# print columns of students
print(colnames(students))
[1] "full_name"  "gender_age" "grade"      "exam"       "score"     

2.The column gender_age sounds like it contains both gender and age!

View the head() of students to see what kind of data gender_age contains.

# view head of students
head(students)

3.It looks like the first character of the values in gender_age contains the gender, while the rest of the string contains the age. Let’s separate out the gender data into a new column called gender. Save the result to students, and view the head().

# add gender and age columns
library(stringr)
students <- students %>%
  mutate(gender = str_sub(gender_age, 1, 1))
head(students)

4.Now, separate out the age data into a new column called age. Save the updated data frame to students, and view the head().

# add gender and age columns
students <- students %>%
  mutate(age = str_sub(gender_age, 2))
head(students)

5.Now, we don’t need that gender_age column anymore. Drop gender_age from students, and save the result to students. View the head() of students.

# drop gender_age column
students <- students %>%
  select(-gender_age)
head(students)

7.Splitting By Character

Let’s say we have a column called “type” with data entries in the format “admin_US” or “user_Kenya”, as shown in the table below.

id type

1011 “user_Kenya”

1112 “admin_US”

1113 “moderator_UK”

Just like we saw before, this column actually contains two types of data. One seems to be the user type (with values like “admin” or “user”) and one seems to be the country this user is in (with values like “US” or “Kenya”).

We can no longer just split along the first 4 characters because admin and user are of different lengths. Instead, we know that we want to split along the “”. We can thus use the tidyr function separate() to split this column into two, separate columns: ” Create the ‘user_type’ and ‘country’ columns //df %>% // separate(type,c(‘user_type’,‘country’),’’)”

1.type is the column to split

2.c(‘user_type’,‘country’) is a vector with the names of the two new columns

3.’_’ is the character to split on

This would transform the table above into a table like:

id user_type country

1011 “user” “Kenya”

1112 “admin” “US”

1113 “moderator” “UK”

Instructions

1.View the head() of students. Notice that the students’ names are stored in a column called full_name.

# view the head of students
head(students)

2.Separate the full_name column into two new columns, first_name and last_name, by splitting on the ’ ’ character .

Provide as an extra argument to the separate() function extra =‘merge’. This will ensure that middle names or two-word last names will all end up in the last_name column.

Save the result to students, and view the head().

# separate the full_name column
students <- students %>%
  separate('full_name', c('first_name', 'last_name'), ' ', extra ='merge')
head(students)

8.Looking at Data Types

Each column of a data frame can hold items of the same data type. The data types that R uses are: character, numeric (real or decimal), integer, logical, or complex. Often, we want to convert between types so that we can do better analysis. If a numerical category like “num_users” is stored as a vector of characters instead of numerics, for example, it makes it more difficult to do something like make a line graph of users over time.

To see the types of each column of a data frame, we can use:

str(df)

str() displays the internal structure of an R object. Calling str() with a data frame as an argument will return a variety of information, including the data types. For a data frame like this:

item price calories

“banana” “$1” 105

“apple” “$0.75” 95

“peach” “$3” 55

“clementine” “$2.5” 35

the data types would be:

// #> $ item: chr

// #> $ price: chr

// #> $ calories: num

We can see that the price column is made up of characters, which will probably make our analysis of price more difficult. We’ll look at how to convert columns into numeric values in the next few exercises.

Instructions

1.Let’s inspect the data types in the students table.

Print out the structure of students.

# print structure of students
str(students)
tibble [2,000 × 7] (S3: tbl_df/tbl/data.frame)
 $ first_name: chr [1:2000] "Moses" "Timofei" "Silvain" "Lezley" ...
 $ last_name : chr [1:2000] "Kirckman" "Strowan" "Poll" "Pinxton" ...
 $ grade     : num [1:2000] 11 11 9 11 11 11 9 11 12 11 ...
 $ exam      : chr [1:2000] "fractions" "fractions" "fractions" "fractions" ...
 $ score     : chr [1:2000] "69%" "63%" "69%" NA ...
 $ gender    : chr [1:2000] "M" "M" "M" "M" ...
 $ age       : chr [1:2000] "14" "18" "18" "18" ...

2.If we wanted to make a scatterplot of age vs average exam score, would we be able to do it with this type of data?

Paste the following code in the last code block to try and print out the mean of the score column of students.

” students %>% summarise(mean_score = mean(score)) ”

What warning do you see?

# mean of age column
students %>% 
  summarise(mean_score = mean(score))

9.String Parsing

Sometimes we need to modify strings in our data frames to help us transform them into more meaningful metrics. For example, in our fruits table from before:

item price calories

“banana” “$1” 105

“apple” “$0.75” 95

“peach” “$3” 55

“peach” “$4” 55

“clementine” “$2.5” 35

We can see that the ‘price’ column is actually composed of character strings representing dollar amounts. This column could be much better represented as numeric, so that we could take the mean, calculate other aggregate statistics, or compare different fruits to one another in terms of price.

First, we can use a regular expression, a sequence of characters that describe a pattern of text to be matched, to remove all of the dollar signs. The base R function gsub() will remove the $ from the price column, replacing the symbol with an empty string ’’:

“fruit %>% mutate(price=gsub(‘\$’,’’,price))”

Then, we can use the base R function as.numeric() to convert character strings containing numerical values to numeric:

“fruit %>% mutate(price = as.numeric(price))”

Now, we have a data frame that looks like: item price calories

“banana” 1 105

“apple” 0.75 95

“peach” 3 55

“peach” 4 55

“clementine” 2.5 35

Instructions

1.We saw in the last exercise that finding the mean of the score column is hard to do when the data is stored as characters and not numbers.

View the head() of students to take a look at the values in the score column.

# view head of students
head(students)

2.Remove the ‘%’ symbol from the score column, and save the resulting data frame to students. View students.

# remove % from score column
students <- students %>%
  mutate(score = gsub('%', '',score))
students

3.Convert the score column to a numerical type using the as.numeric() function. Save this new data frame to students, and view

# change score column to numeric
students <- students %>%
  mutate(score = as.numeric(score))
students

10.Review

Great! We have looked at a number of different methods we may use to get data into the format we want for analysis.

Specifically, we have covered:

1.diagnosing the “tidiness” of data

2.combining multiple files

3.reshaping data

4.changing the types of values

5.manipulating strings to represent data better

You can use these methods to transform your datasets to be clean and easy to work with!

Instructions

1.The students data frame is nearly cleaned and ready for analysis! There’s one more change that can be made to the ages of the students to help describe and visualize the data. What could that change be? What is the ideal data type for the ages column?

Make the change to age and save the resulting data frame to students.

# update age column
students <- students %>%
  mutate(age = as.numeric(age));
students
LS0tDQp0aXRsZTogIkRhdGEgQ2xlYW5pbmcgaW4gUiINCmF1dGhvcjogIkFubmFiZWwgS3VvIg0KZGF0ZTogImByIGZvcm1hdChTeXMudGltZSgpLCAnJVktJW0tJWQgJUg6JU0nKWAiDQpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sNCi0tLQ0KYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0NCiMgbG9hZCBsaWJyYXJpZXMNCmxpYnJhcnkocmVhZHIpDQpsaWJyYXJ5KGRwbHlyKQ0KYGBgDQoNCiMgMS5JbnRyb2R1Y3Rpb24NCg0KQSBodWdlIHBhcnQgb2YgZGF0YSBzY2llbmNlIGludm9sdmVzIGFjcXVpcmluZyByYXcgZGF0YSBhbmQgZ2V0dGluZyBpdCBpbnRvIGEgZm9ybSByZWFkeSBmb3IgYW5hbHlzaXMuIFNvbWUgaGF2ZSBlc3RpbWF0ZWQgdGhhdCBkYXRhIHNjaWVudGlzdHMgc3BlbmQgODAlIG9mIHRoZWlyIHRpbWUgY2xlYW5pbmcgYW5kIG1hbmlwdWxhdGluZyBkYXRhLCBhbmQgb25seSAyMCUgb2YgdGhlaXIgdGltZSBhY3R1YWxseSBhbmFseXppbmcgaXQgb3IgYnVpbGRpbmcgbW9kZWxzIGZyb20gaXQuDQoNCldoZW4gd2UgcmVjZWl2ZSByYXcgZGF0YSwgd2UgaGF2ZSB0byBkbyBhIG51bWJlciBvZiB0aGluZ3MgYmVmb3JlIHdl4oCZcmUgcmVhZHkgdG8gYW5hbHl6ZSBpdCwgcG9zc2libHkgaW5jbHVkaW5nOg0KDQoxLmRpYWdub3NpbmcgdGhlIOKAnHRpZGluZXNz4oCdIG9mIHRoZSBkYXRhIOKAlCBob3cgbXVjaCBkYXRhIGNsZWFuaW5nIHdlIHdpbGwgaGF2ZSB0byBkbw0KDQoyLnJlc2hhcGluZyB0aGUgZGF0YSDigJQgZ2V0dGluZyB0aGUgcmlnaHQgcm93cyBhbmQgY29sdW1ucyBmb3IgZWZmZWN0aXZlIGFuYWx5c2lzDQoNCjMuY29tYmluaW5nIG11bHRpcGxlIGZpbGVzDQoNCjQuY2hhbmdpbmcgdGhlIHR5cGVzIG9mIHZhbHVlcyDigJQgaG93IHdlIGZpeCBhIGNvbHVtbiB3aGVyZSBudW1lcmljYWwgdmFsdWVzIGFyZSBzdG9yZWQgYXMgUHJldmlldzogRG9jcyBMb2FkaW5nIGxpbmsgZGVzY3JpcHRpb24gc3RyaW5ncywgZm9yIGV4YW1wbGUNCg0KNS5kcm9wcGluZyBvciBmaWxsaW5nIG1pc3NpbmcgdmFsdWVzIC0gaG93IHdlIGRlYWwgd2l0aCBkYXRhIHRoYXQgaXMgaW5jb21wbGV0ZSBvciBtaXNzaW5nDQoNCjYubWFuaXB1bGF0aW5nIHN0cmluZ3MgdG8gcmVwcmVzZW50IHRoZSBkYXRhIGJldHRlcg0KDQpXZSB3aWxsIGdvIHRocm91Z2ggdGhlIHRlY2huaXF1ZXMgZGF0YSBzY2llbnRpc3RzIHVzZSB0byBhY2NvbXBsaXNoIHRoZXNlIGdvYWxzIGJ5IGxvb2tpbmcgYXQgc29tZSDigJx1bmNsZWFu4oCdIGRhdGFzZXRzIGFuZCB0cnlpbmcgdG8gZ2V0IHRoZW0gaW50byBhIGdvb2QsIGNsZWFuIHN0YXRlLiBBbG9uZyB0aGUgd2F5IHdlIHdpbGwgdXNlIHRoZSBwb3dlcmZ1bCB0aWR5dmVyc2UgcGFja2FnZXMgZHBseXIgYW5kIHRpZHlyIHRvIGdldCBvdXIgZGF0YSBzcXVlYWt5IGNsZWFuIQ0KDQojIyBJbnN0dWN0aW9ucw0KDQpXZSBoYXZlIHByb3ZpZGVkIGFuIGV4YW1wbGUgb2YgZGF0YSByZXByZXNlbnRpbmcgZXhhbSBzY29yZXMgZnJvbSAxMDAwIHN0dWRlbnRzIGluIGFuIG9ubGluZSBtYXRoIGNsYXNzLg0KDQpUaGVzZSBkYXRhIGZyYW1lcywgd2hpY2ggeW91IGNhbiB2aWV3IGluIHRoZSByZW5kZXJlZCBub3RlYm9vaywgYXJlIGhhcmQgdG8gd29yayB3aXRoLiBUaGV54oCZcmUgc2VwYXJhdGVkIGludG8gbXVsdGlwbGUgdGFibGVzLCBhbmQgdGhlIHZhbHVlcyBkb27igJl0IGxlbmQgdGhlbXNlbHZlcyB3ZWxsIHRvIGFuYWx5c2lzLiBUcnkgdG8gdGhpbmsgYWJvdXQgaG93IHlvdSB3b3VsZCBwbG90IHRoZSBleGFtIHNjb3JlIGF2ZXJhZ2UgYWdhaW5zdCB0aGUgYWdlIG9mIHRoZSBzdHVkZW50cyBpbiB0aGUgY2xhc3MuIFRoaXMgd291bGQgbm90IGJlIGVhc3khDQoNCkluIHRoZSBuZXh0IGV4ZXJjaXNlcywgd2XigJlsbCB0cmFuc2Zvcm0gdGhpcyBkYXRhIHNvIHRoYXQgcGVyZm9ybWluZyBhIHRhc2sgbGlrZSB0aGF0IHZpc3VhbGl6YXRpb24gd291bGQgYmUgc2ltcGxlLg0KDQpgYGB7ciBtZXNzYWdlPUZBTFNFfQ0KIyBsb2FkIGRhdGEgZnJhbWUNCnN0dWRlbnRzXzEgPC0gcmVhZF9jc3YoJ2V4YW1zXzAuY3N2JykNCnN0dWRlbnRzXzIgPC0gcmVhZF9jc3YoJ2V4YW1zXzEuY3N2JykNCmBgYA0KDQpgYGB7cn0NCiMgaW5zcGVjdCBkYXRhIGZyYW1lDQpoZWFkKHN0dWRlbnRzXzEpDQpoZWFkKHN0dWRlbnRzXzIpDQpgYGANCg0KIyAyLkRpYWdub3NlIHRoZSBEYXRhDQoNCldlIG9mdGVuIGRlc2NyaWJlIGRhdGEgdGhhdCBpcyBlYXN5IHRvIGFuYWx5emUgYW5kIHZpc3VhbGl6ZSBhcyDigJx0aWR5IGRhdGHigJ0uIFdoYXQgZG9lcyBpdCBtZWFuIHRvIGhhdmUgdGlkeSBkYXRhPw0KDQpGb3IgZGF0YSB0byBiZSB0aWR5LCBpdCBtdXN0IGhhdmU6DQoNCjEuRWFjaCB2YXJpYWJsZSBhcyBhIHNlcGFyYXRlIGNvbHVtbg0KDQoyLkVhY2ggcm93IGFzIGEgc2VwYXJhdGUgb2JzZXJ2YXRpb24NCg0KVGhlIGZpcnN0IHN0ZXAgb2YgZGlhZ25vc2luZyB3aGV0aGVyIG9yIG5vdCBhIGRhdGFzZXQgaXMgdGlkeSBpcyB1c2luZyBiYXNlIFIgYW5kIGRwbHlyIGZ1bmN0aW9ucyB0byBleHBsb3JlIGFuZCBwcm9iZSB0aGUgZGF0YXNldC4NCg0KWW914oCZdmUgc2VlbiBtb3N0IG9mIHRoZSBmdW5jdGlvbnMgd2Ugb2Z0ZW4gdXNlIHRvIGRpYWdub3NlIGEgZGF0YXNldCBmb3IgY2xlYW5pbmcuIFNvbWUgb2YgdGhlIG1vc3QgdXNlZnVsIG9uZXMgYXJlOg0KDQoxLmhlYWQoKSDigJQgZGlzcGxheSB0aGUgZmlyc3QgNiByb3dzIG9mIHRoZSB0YWJsZQ0KDQoyLnN1bW1hcnkoKSDigJQgZGlzcGxheSB0aGUgc3VtbWFyeSBzdGF0aXN0aWNzIG9mIHRoZSB0YWJsZQ0KDQozLmNvbG5hbWVzKCkg4oCUIGRpc3BsYXkgdGhlIGNvbHVtbiBuYW1lcyBvZiB0aGUgdGFibGUNCg0KYGBge3IgbWVzc2FnZT1GQUxTRX0NCiMgcmVhZCBDU1ZzDQpncm9jZXJ5XzEgPC0gcmVhZF9jc3YoJ2dyb2NlcnlfMS5jc3YnKQ0KZ3JvY2VyeV8yIDwtIHJlYWRfY3N2KCdncm9jZXJ5XzIuY3N2JykNCmBgYA0KDQojIyBJbnN0cnVjdGlvbnMNCg0KMS5Qcm92aWRlZCBpbiBub3RlYm9vay5SbWQgYXJlIHR3byBkYXRhIGZyYW1lcywgZ3JvY2VyeV8xIGFuZCBncm9jZXJ5XzIuDQoNCkJlZ2luIGJ5IHZpZXdpbmcgdGhlIGhlYWQoKSBvZiBib3RoIGdyb2NlcnlfMSBhbmQgZ3JvY2VyeV8yLg0KDQpgYGB7cn0NCiMgaW5zcGVjdCBkYXRhIGZyYW1lcw0KaGVhZChncm9jZXJ5XzEpDQpoZWFkKGdyb2NlcnlfMikNCnN1bW1hcnkoZ3JvY2VyeV8xKQ0Kc3VtbWFyeShncm9jZXJ5XzIpDQpjb2xuYW1lcyhncm9jZXJ5XzEpDQpjb2xuYW1lcyhncm9jZXJ5XzIpDQpgYGANCg0KMi5FeHBsb3JlIHRoZSBkYXRhIGZyYW1lcyB1c2luZyB0aGUgb3RoZXIgZnVuY3Rpb25zIGxpc3RlZC4NCg0KV2hpY2ggZGF0YSBmcmFtZSBpcyDigJxjbGVhbuKAnSwgdGlkeSwgYW5kIHJlYWR5IGZvciBhbmFseXNpcz8gQ3JlYXRlIGEgdmFyaWFibGUgbmFtZWQgY2xlYW5fZGF0YV9mcmFtZSBhbmQgYXNzaWduIGl0IHRoZSB2YWx1ZSAxIGlmIGdyb2NlcnlfMSBpcyBhIGNsZWFuIGFuZCB0aWR5IGRhdGEgZnJhbWUgb3IgMiBpZiBncm9jZXJ5XzIgaXMgYSBjbGVhbiBhbmQgdGlkeSBkYXRhIGZyYW1lLg0KDQpgYGB7cn0NCiMgY2xlYW4gZGF0YSBmcmFtZQ0KY2xlYW5fZGF0YV9mcmFtZSA8LSAyDQpgYGANCg0KIyAzLkRlYWxpbmcgd2l0aCBNdWx0aXBsZSBGaWxlcw0KT2Z0ZW4sIHlvdSBoYXZlIHRoZSBzYW1lIGRhdGEgc2VwYXJhdGVkIG91dCBpbnRvIG11bHRpcGxlIGZpbGVzLg0KDQpMZXTigJlzIHNheSB0aGF0IHlvdSBoYXZlIGEgdG9uIG9mIGZpbGVzIGZvbGxvd2luZyB0aGUgZmlsZW5hbWUgc3RydWN0dXJlOiAnZmlsZV8xLmNzdicsICdmaWxlXzIuY3N2JywgJ2ZpbGVfMy5jc3YnLCBhbmQgc28gb24uIFRoZSBwb3dlciBvZiBkcGx5ciBhbmQgdGlkeXIgaXMgbWFpbmx5IGluIGJlaW5nIGFibGUgdG8gbWFuaXB1bGF0ZSBsYXJnZSBhbW91bnRzIG9mIHN0cnVjdHVyZWQgZGF0YSwgc28geW91IHdhbnQgdG8gYmUgYWJsZSB0byBnZXQgYWxsIG9mIHRoZSByZWxldmFudCBpbmZvcm1hdGlvbiBpbnRvIG9uZSB0YWJsZSBzbyB0aGF0IHlvdSBjYW4gYW5hbHl6ZSB0aGUgYWdncmVnYXRlIGRhdGEuDQoNCllvdSBjYW4gY29tYmluZSB0aGUgYmFzZSBSIGZ1bmN0aW9ucyBsaXN0LmZpbGVzKCkgYW5kIGxhcHBseSgpIHdpdGggcmVhZHIgYW5kIGRwbHlyIHRvIG9yZ2FuaXplIHRoaXMgZGF0YSBiZXR0ZXIsIGFzIHNob3duIGJlbG93Og0KIg0KZmlsZXMgPC0gbGlzdC5maWxlcyhwYXR0ZXJuID0gImZpbGVfLipjc3YiKQ0KZGZfbGlzdCA8LSBsYXBwbHkoZmlsZXMscmVhZF9jc3YpDQpkZiA8LSBiaW5kX3Jvd3MoZGZfbGlzdCkNCiINCjEuVGhlIGZpcnN0IGxpbmUgdXNlcyBsaXN0LmZpbGVzKCkgYW5kIGEgcmVndWxhciBleHByZXNzaW9uLCBhIHNlcXVlbmNlIG9mIGNoYXJhY3RlcnMgZGVzY3JpYmluZyBhIHBhdHRlcm4gb2YgdGV4dCB0aGF0IHNob3VsZCBiZSBtYXRjaGVkLCB0byBmaW5kIGFueSBmaWxlIGluIHRoZSBjdXJyZW50IGRpcmVjdG9yeSB0aGF0IHN0YXJ0cyB3aXRoICdmaWxlXycgYW5kIGhhcyBhbiBleHRlbnNpb24gb2YgY3N2LCBzdG9yaW5nIHRoZSBuYW1lIG9mIGVhY2ggZmlsZSBpbiBhIHZlY3RvciBmaWxlcw0KDQoyLlRoZSBzZWNvbmQgbGluZSB1c2VzIGxhcHBseSgpIHRvIHJlYWQgZWFjaCBmaWxlIGluIGZpbGVzIGludG8gYSBkYXRhIGZyYW1lIHdpdGggcmVhZF9jc3YoKSwgc3RvcmluZyB0aGUgDQpkYXRhIGZyYW1lcyBpbiBkZl9saXN0DQoNCjMuVGhlIHRoaXJkIGxpbmUgdGhlbiBjb25jYXRlbmF0ZXMgYWxsIG9mIHRob3NlIGRhdGEgZnJhbWVzIHRvZ2V0aGVyIHdpdGggZHBseXLigJlzIGJpbmRfcm93cygpIGZ1bmN0aW9uDQoNCiMjIEluc3RydWN0aW9ucw0KMS5Zb3UgaGF2ZSAxMCBkaWZmZXJlbnQgZmlsZXMgY29udGFpbmluZyAxMDAgc3R1ZGVudHMgZWFjaC4gVGhlc2UgZmlsZXMgZm9sbG93IHRoZSBuYW1pbmcgc3RydWN0dXJlOg0KDQpleGFtc18wLmNzdg0KZXhhbXNfMS5jc3YNCuKApiB1cCB0byBleGFtc185LmNzdg0KDQpZb3UgYXJlIGdvaW5nIHRvIHJlYWQgZWFjaCBmaWxlIGludG8gYW4gaW5kaXZpZHVhbCBkYXRhIGZyYW1lIGFuZCB0aGVuIGNvbWJpbmUgYWxsIG9mIHRoZSBlbnRyaWVzIGludG8gb25lIGRhdGEgZnJhbWUuDQoNCkZpcnN0LCBjcmVhdGUgYSB2YXJpYWJsZSBjYWxsZWQgc3R1ZGVudF9maWxlcyBhbmQgc2V0IGl0IGVxdWFsIHRvIHRoZSBsaXN0LmZpbGVzKCkgb2YgYWxsIG9mIHRoZSBDU1YgZmlsZXMgd2Ugd2FudCB0byBpbXBvcnQuDQpgYGB7cn0NCiMgbGlzdCBmaWxlcw0Kc3R1ZGVudF9maWxlcyA8LSBsaXN0LmZpbGVzKHBhdHRlcm4gPSAiZXhhbXNfLipjc3YiKQ0Kc3R1ZGVudF9maWxlcw0KYGBgDQoyLlJlYWQgZWFjaCBmaWxlIGluIHN0dWRlbnRfZmlsZXMgaW50byBhIGRhdGEgZnJhbWUgdXNpbmcgbGFwcGx5KCkgYW5kIHNhdmUgdGhlIHJlc3VsdCB0byBkZl9saXN0Lg0KYGBge3J9DQojIHJlYWQgZmlsZXMNCmRmX2xpc3QgPC0gbGFwcGx5KHN0dWRlbnRfZmlsZXMsIHJlYWRfY3N2KQ0KZGZfbGlzdA0KYGBgDQozLkNvbmNhdGVuYXRlIGFsbCBvZiB0aGUgZGF0YSBmcmFtZXMgaW4gZGZfbGlzdCBpbnRvIG9uZSBkYXRhIGZyYW1lIGNhbGxlZCBzdHVkZW50cy4NCmBgYHtyfQ0KIyBjb25jYXRlbmF0ZSBkYXRhIGZyYW1lcw0Kc3R1ZGVudHMgPC0gYmluZF9yb3dzKGRmX2xpc3QpDQpzdHVkZW50cw0KYGBgDQo0Lkluc3BlY3Qgc3R1ZGVudHMuIFNhdmUgdGhlIG51bWJlciBvZiByb3dzIGluIHN0dWRlbnRzIHRvIG5yb3dfc3R1ZGVudHMuIERpZCB5b3UgZ2V0IGFsbCBvZiB0aGVtDQpgYGB7cn0NCiMgbnVtYmVyIG9mIHJvd3MgaW4gc3R1ZGVudHMNCm5yb3dfc3R1ZGVudHMgPC0gbnJvdyhzdHVkZW50cykNCnByaW50KG5yb3dfc3R1ZGVudHMpDQpgYGANCiMgNC5SZXNoYXBpbmcgeW91ciBEYXRhDQpTaW5jZSB3ZSB3YW50DQoNCjEuRWFjaCB2YXJpYWJsZSBhcyBhIHNlcGFyYXRlIGNvbHVtbg0KDQoyLkVhY2ggcm93IGFzIGEgc2VwYXJhdGUgb2JzZXJ2YXRpb24NCg0KV2Ugd291bGQgd2FudCB0byByZXNoYXBlIGEgdGFibGUgbGlrZToNCg0KQWNjb3VudAkgICAgQ2hlY2tpbmcJU2F2aW5ncw0KDQrigJwxMjQ1NjU0M+KAnQkgODUwMAkgICAgICAgODkwMA0KDQrigJwxMjI4Mzk0MuKAnQkgNjQxMAkgICAgICAgODAyMA0KDQrigJwxMjgzOTQ4NeKAnQk3ODAwMAkgICAgICA5MjAwMA0KDQpJbnRvIGEgdGFibGUgdGhhdCBsb29rcyBtb3JlIGxpa2U6DQoNCkFjY291bnQJICAgIEFjY291bnQgVHlwZQkgIEFtb3VudA0KDQrigJwxMjQ1NjU0M+KAnQnigJxDaGVja2luZ+KAnQkgICAgICA4NTAwDQoNCuKAnDEyNDU2NTQz4oCdCeKAnFNhdmluZ3PigJ0JICAgICAgICA4OTAwDQoNCuKAnDEyMjgzOTQy4oCdCeKAnENoZWNraW5n4oCdCSAgICAgIDY0MTANCg0K4oCcMTIyODM5NDLigJ0J4oCcU2F2aW5nc+KAnQkgICAgICAgIDgwMjANCg0K4oCcMTI4Mzk0ODXigJ0J4oCcQ2hlY2tpbmfigJ0JICAgICA3ODAwMA0KDQrigJwxMjgzOTQ4NeKAnQnigJxTYXZpbmdz4oCdCSAgICAgIDkyMDAwMA0KDQoNCldlIGNhbiB1c2UgdGlkeXLigJlzIGdhdGhlcigpIGZ1bmN0aW9uIHRvIGRvIHRoaXMgdHJhbnNmb3JtYXRpb24uIGdhdGhlcigpIHRha2VzIGEgZGF0YSBmcmFtZSBhbmQgdGhlIGNvbHVtbnMgdG8gdW5wYWNrOg0KYGBge3J9DQpsaWJyYXJ5KHRpZHlyKQ0KYGBgDQoNCiINCmRmICU+JQ0KICBnYXRoZXIoJ0NoZWNraW5nJywnU2F2aW5ncycsa2V5PSdBY2NvdW50IFR5cGUnLHZhbHVlPSdBbW91bnQnKQ0KIg0KVGhlIGFyZ3VtZW50cyB5b3UgcHJvdmlkZSBhcmU6DQoNCjEuZGY6IHRoZSBkYXRhIGZyYW1lIHlvdSB3YW50IHRvIGdhdGhlciwgd2hpY2ggY2FuIGJlIHBpcGVkIGludG8gZ2F0aGVyKCkNCg0KMi5DaGVja2luZyBhbmQgU2F2aW5nczogdGhlIGNvbHVtbnMgb2YgdGhlIG9sZCBkYXRhIGZyYW1lIHRoYXQgeW91IHdhbnQgdG8gdHVybiBpbnRvIHZhcmlhYmxlcw0KDQozLmtleTogd2hhdCB0byBjYWxsIHRoZSBjb2x1bW4gb2YgdGhlIG5ldyBkYXRhIGZyYW1lIHRoYXQgc3RvcmVzIHRoZSB2YXJpYWJsZXMNCg0KNC52YWx1ZTogd2hhdCB0byBjYWxsIHRoZSBjb2x1bW4gb2YgdGhlIG5ldyBkYXRhIGZyYW1lIHRoYXQgc3RvcmVzIHRoZSB2YWx1ZXMNCg0KIyMgSW5zdHJ1Y3Rpb25zDQoxLlRoZSBzdHVkZW50cyBkYXRhIGZyYW1lIGZyb20gdGhlIHByZXZpb3VzIGV4ZXJjaXNlIGhhcyBiZWVuIGxvYWRlZCBpbnRvIHRoZSBub3RlYm9vayBmb3IgeW91LiBTYXZlIHRoZSBjb2x1bW5zIG5hbWVzIHRvIG9yaWdpbmFsX2NvbF9uYW1lcyBhbmQgcHJpbnQgaXQuDQpgYGB7cn0NCiMgb3JpZ2luYWwgY29sdW1uIG5hbWVzDQpvcmlnaW5hbF9jb2xfbmFtZXMgPC0gY29sbmFtZXMoc3R1ZGVudHMpDQpvcmlnaW5hbF9jb2xfbmFtZXMNCmBgYA0KMi5UaGVyZSBpcyBhIGNvbHVtbiBmb3IgdGhlIHNjb3JlcyBvbiB0aGUgZnJhY3Rpb25zIGV4YW0sIGFuZCBhIGNvbHVtbiBmb3IgdGhlIHNjb3JlcyBvbiB0aGUgcHJvYmFiaWxpdHkgZXhhbS4NCg0KV2Ugd2FudCB0byBtYWtlIGVhY2ggcm93IGFuIG9ic2VydmF0aW9uLCBzbyB3ZSB3YW50IHRvIHRyYW5zZm9ybSB0aGlzIHRhYmxlIHRvIGxvb2sgbGlrZToNCg0KZnVsbF9uYW1lCSAgICAgICAgZXhhbQkgICAgc2NvcmUJIGdlbmRlcl9hZ2UJZ3JhZGUNCg0K4oCcRmlyc3QgU3R1ZGVudOKAnQnigJxmcmFjdGlvbnPigJ0Jc2NvcmUlCSAgICDigKYJICAgICAg4oCmDQoNCuKAnEZpcnN0IFN0dWRlbnTigJ0J4oCccHJvYmFiaWxpdHnigJ0Jc2NvcmUlICAJ4oCmCSAgICAgIOKApg0KDQrigJxTZWNvbmQgU3R1ZGVudOKAnQnigJxmcmFjdGlvbnPigJ0Jc2NvcmUlCSAg4oCmCSAgICAgIOKApg0KDQrigJxTZWNvbmQgU3R1ZGVudOKAnQnigJxwcm9iYWJpbGl0eeKAnQlzY29yZSUJ4oCmCSAgICAgIOKApg0KDQoNClVzZSBnYXRoZXIgdG8gY3JlYXRlIGEgbmV3IHRhYmxlIChzdGlsbCBjYWxsZWQgc3R1ZGVudHMpIHRoYXQgZm9sbG93cyB0aGlzIHN0cnVjdHVyZS4gVGhlbiB2aWV3IHRoZSBoZWFkKCkgb2Ygc3R1ZGVudHMuDQoNCmBgYHtyfQ0KIyBnYXRoZXIgY29sdW1ucw0Kc3R1ZGVudHMgPC0gc3R1ZGVudHMgJT4lDQogIGdhdGhlcignZnJhY3Rpb25zJywgJ3Byb2JhYmlsaXR5Jywga2V5ID0gJ2V4YW0nLCB2YWx1ZSA9ICdzY29yZScpDQpoZWFkKHN0dWRlbnRzKQ0KYGBgDQozLlNhdmUgdGhlIGNvbHVtbnMgbmFtZXMgb2YgdGhlIHVwZGF0ZWQgc3R1ZGVudHMgZGF0YSBmcmFtZSB0byBnYXRoZXJlZF9jb2xfbmFtZXMgYW5kIHByaW50IGl0Lg0KYGBge3J9DQojIHVwZGF0ZWQgY29sdW1uIG5hbWVzDQpnYXRoZXJlZF9jb2xfbmFtZXMgPC0gY29sbmFtZXMoc3R1ZGVudHMpDQpnYXRoZXJlZF9jb2xfbmFtZXMNCmBgYA0KNC5UaGUgZHBseXIgZnVuY3Rpb24gY291bnQoKSB0YWtlcyBhIGRhdGEgZnJhbWUgYW5kIGEgY29sdW1uIGFzIGFyZ3VtZW50cyBhbmQgcmV0dXJucyBhIHRhYmxlIHdpdGggY291bnRzIG9mIHRoZSB1bmlxdWUgdmFsdWVzIGluIHRoZSBuYW1lZCBjb2x1bW4uDQoNCkZpbmQgdGhlIGNvdW50IG9mIGVhY2ggdW5pcXVlIHZhbHVlIGluIHRoZSBleGFtIGNvbHVtbi4gU2F2ZSB0aGUgcmVzdWx0IHRvIGV4YW1fY291bnRzIGFuZCB2aWV3IGV4YW1fY291bnRzLg0KYGBge3J9DQojIHVuaXF1ZSB2YWx1ZSBjb3VudHMgb2YgZXhhbQ0KZXhhbV9jb3VudHMgPC0gc3R1ZGVudHMgJT4lDQogIGNvdW50KGV4YW0pDQpleGFtX2NvdW50cw0KYGBgDQojIDUuRGVhbGluZyB3aXRoIER1cGxpY2F0ZXMNCk9mdGVuIHdlIHNlZSBkdXBsaWNhdGVkIHJvd3Mgb2YgZGF0YSBpbiB0aGUgZGF0YSBmcmFtZXMgd2UgYXJlIHdvcmtpbmcgd2l0aC4gVGhpcyBjb3VsZCBoYXBwZW4gZHVlIHRvIGVycm9ycyBpbiBkYXRhIGNvbGxlY3Rpb24gb3IgaW4gc2F2aW5nIGFuZCBsb2FkaW5nIHRoZSBkYXRhLg0KDQpUbyBjaGVjayBmb3IgZHVwbGljYXRlcywgd2UgY2FuIHVzZSB0aGUgYmFzZSBSIGZ1bmN0aW9uIGR1cGxpY2F0ZWQoKSwgd2hpY2ggd2lsbCByZXR1cm4gYSBsb2dpY2FsIHZlY3RvciB0ZWxsaW5nIHVzIHdoaWNoIHJvd3MgYXJlIGR1cGxpY2F0ZSByb3dzLg0KDQpMZXTigJlzIHNheSB3ZSBoYXZlIGEgZGF0YSBmcmFtZSBmcnVpdHMgdGhhdCByZXByZXNlbnRzIHRoaXMgdGFibGU6DQoNCml0ZW0JICAgICAgICAgcHJpY2UJY2Fsb3JpZXMNCg0K4oCcYmFuYW5h4oCdCSAgICAgIOKAnCQx4oCdCSAgICAgMTA1DQoNCuKAnGFwcGxl4oCdCSAgICAg4oCcJDAuNzXigJ0JICAgICAgOTUNCg0K4oCcYXBwbGXigJ0JICAgICDigJwkMC43NeKAnQkgICAgICA5NQ0KDQrigJxwZWFjaOKAnQkgICAgICAgIOKAnCQz4oCdCSAgICAgIDU1DQoNCuKAnHBlYWNo4oCdCSAgICAgICAg4oCcJDTigJ0JICAgICAgNTUNCg0K4oCcY2xlbWVudGluZeKAnQnigJwkMi414oCdCSAgICAgIDM1DQoNCklmIHdlIGNhbGwgZnJ1aXRzICU+JSBkdXBsaWNhdGVkKCksIHdlIHdvdWxkIGdldCB0aGUgZm9sbG93aW5nIHZlY3RvcjoNCg0KIj4+IFsxXSBGQUxTRSBGQUxTRSBUUlVFIEZBTFNFIEZBTFNFIEZBTFNFIg0KDQpXZSBjYW4gc2VlIHRoYXQgdGhlIHRoaXJkIHJvdywgd2hpY2ggcmVwcmVzZW50cyBhbiAiYXBwbGUiIHdpdGggcHJpY2UgIiQwLjc1IiBhbmQgOTUgY2Fsb3JpZXMsIGlzIGEgZHVwbGljYXRlIHJvdy4gRXZlcnkgdmFsdWUgaW4gdGhpcyByb3cgaXMgdGhlIHNhbWUgYXMgaW4gYW5vdGhlciByb3cgKHRoZSBwcmV2aW91cyByb3cpLg0KDQpXZSBjYW4gdXNlIHRoZSBkcGx5ciBkaXN0aW5jdCgpIGZ1bmN0aW9uIHRvIHJlbW92ZSBhbGwgcm93cyBvZiBhIGRhdGEgZnJhbWUgdGhhdCBhcmUgZHVwbGljYXRlcyBvZiBhbm90aGVyIHJvdy4NCg0KSWYgd2UgY2FsbCBmcnVpdHMgJT4lIGRpc3RpbmN0KCksIHdlIHdvdWxkIGdldCB0aGUgdGFibGU6DQoNCml0ZW0JICAgICAgICAgcHJpY2UJIGNhbG9yaWVzDQoNCuKAnGJhbmFuYeKAnQkgICAgICDigJwkMeKAnQkgICAgICAxMDUNCg0K4oCcYXBwbGXigJ0JICAgICDigJwkMC43NeKAnQkgICAgICAgOTUNCg0K4oCccGVhY2jigJ0JICAgICAgICDigJwkM+KAnQkgICAgICAgNTUNCg0K4oCccGVhY2jigJ0JICAgICAgICDigJwkNOKAnQkgICAgICAgNTUNCg0K4oCcY2xlbWVudGluZeKAnQnigJwkMi414oCdCSAgICAgICAzNQ0KDQpUaGUgImFwcGxlIiByb3cgd2FzIGRlbGV0ZWQgYmVjYXVzZSBpdCB3YXMgZXhhY3RseSB0aGUgc2FtZSBhcyBhbm90aGVyIHJvdy4gQnV0IHRoZSB0d28gInBlYWNoIiByb3dzIHJlbWFpbiBiZWNhdXNlIHRoZXJlIGlzIGEgZGlmZmVyZW5jZSBpbiB0aGUgcHJpY2UgY29sdW1uLg0KDQpJZiB3ZSB3YW50ZWQgdG8gcmVtb3ZlIGV2ZXJ5IHJvdyB3aXRoIGEgZHVwbGljYXRlIHZhbHVlIGluIHRoZSBpdGVtIGNvbHVtbiwgd2UgY291bGQgc3BlY2lmeSBhIHN1YnNldDoNCg0KImZydWl0cyAlPiUNCiAgZGlzdGluY3QoaXRlbSwua2VlcF9hbGw9VFJVRSkiDQoNCkJ5IGRlZmF1bHQsIHRoaXMga2VlcHMgdGhlIGZpcnN0IG9jY3VycmVuY2Ugb2YgdGhlIGR1cGxpY2F0ZToNCg0KaXRlbQkgICAgICAgcHJpY2UJICAgY2Fsb3JpZXMNCg0K4oCcYmFuYW5h4oCdCSAgIOKAnCQx4oCdCSAgICAgMTA1DQoNCuKAnGFwcGxl4oCdCSAgICAg4oCcJDAuNzXigJ0JIDk1DQoNCuKAnHBlYWNo4oCdCSAgICAg4oCcJDPigJ0JICAgICA1NQ0KDQrigJxjbGVtZW50aW5l4oCdIOKAnCQyLjXigJ0JICAgMzUNCg0KTWFrZSBzdXJlIHRoYXQgdGhlIGNvbHVtbnMgeW91IGRyb3AgZHVwbGljYXRlcyBmcm9tIGFyZSBzcGVjaWZpY2FsbHkgdGhlIG9uZXMgd2hlcmUgZHVwbGljYXRlcyBkb27igJl0IGJlbG9uZy4gWW91IHdvdWxkbuKAmXQgd2FudCB0byBkcm9wIGR1cGxpY2F0ZXMgd2l0aCB0aGUgcHJpY2UgY29sdW1uIGFzIGEgc3Vic2V0LCBmb3IgZXhhbXBsZSwgYmVjYXVzZSBpdOKAmXMgb2theSBpZiBtdWx0aXBsZSBpdGVtcyBjb3N0IHRoZSBzYW1lIGFtb3VudCENCg0KIyMgSW5zdHJ1Y3Rpb25zDQoxLlRoZSBzdHVkZW50cyBkYXRhIGZyYW1lIGhhcyBhIGNvbHVtbiBpZCB0aGF0IGlzIG5laXRoZXIgdW5pcXVlIG5vciByZXF1aXJlZCBmb3Igb3VyIGFuYWx5c2lzLiBEcm9wIHRoZSBpZCBjb2x1bW4gZnJvbSB0aGUgZGF0YSBmcmFtZSBhbmQgc2F2ZSB0aGUgcmVzdWx0IHRvIHN0dWRlbnRzLiBWaWV3IHRoZSBoZWFkKCkgb2Ygc3R1ZGVudHMuDQpgYGB7cn0NCiMgZHJvcCBpZCBjb2x1bW4NCnN0dWRlbnRzIDwtIHN0dWRlbnRzICU+JQ0KICBzZWxlY3QoLWlkKQ0KaGVhZChzdHVkZW50cykNCmBgYA0KMi4NCkl0IHNlZW1zIGxpa2UgaW4gdGhlIGRhdGEgY29sbGVjdGlvbiBwcm9jZXNzLCBzb21lIHJvd3MgbWF5IGhhdmUgYmVlbiByZWNvcmRlZCB0d2ljZS4gVXNlIHRoZSBkdXBsaWNhdGVkKCkgZnVuY3Rpb24gb24gdGhlIHN0dWRlbnRzIGRhdGEgZnJhbWUgdG8gbWFrZSBhIHZlY3RvciBvYmplY3QgY2FsbGVkIGR1cGxpY2F0ZXMuDQpgYGB7cn0NCiMgZmluZCBkdXBsaWNhdGVkIHJvd3MNCmR1cGxpY2F0ZXMgPC0gc3R1ZGVudHMgJT4lDQogIGR1cGxpY2F0ZWQoKQ0KaGVhZChkdXBsaWNhdGVzKQ0KYGBgDQozLnRhYmxlKCkgaXMgYSBiYXNlIFIgZnVuY3Rpb24gdGhhdCB0YWtlcyBhbnkgUiBvYmplY3QgYXMgYW4gYXJndW1lbnQgYW5kIHJldHVybnMgYSB0YWJsZSB3aXRoIHRoZSBjb3VudHMgb2YgZWFjaCB1bmlxdWUgdmFsdWUgaW4gdGhlIG9iamVjdC4NCg0KUGlwZSB0aGUgcmVzdWx0IGZyb20gdGhlIHByZXZpb3VzIGNoZWNrcG9pbnQgaW50byB0YWJsZSgpIHRvIHNlZSBob3cgbWFueSByb3dzIGFyZSBleGFjdCBkdXBsaWNhdGVzLiBNYWtlIHN1cmUgdG8gc2F2ZSB0aGUgcmVzdWx0IHRvIGR1cGxpY2F0ZV9jb3VudHMsIGFuZCB2aWV3IGR1cGxpY2F0ZV9jb3VudHMuDQpgYGB7cn0NCiMgY291bnQgZHVwbGljYXRlZCByb3dzDQpkdXBsaWNhdGVfY291bnRzIDwtIGR1cGxpY2F0ZXMgJT4lDQogIHRhYmxlKCkNCmR1cGxpY2F0ZV9jb3VudHMNCmBgYA0KDQo0LkdldCByaWQgb2YgdGhlIGR1cGxpY2F0ZSByb3dzIGluIHRoZSBzdHVkZW50cyBkYXRhIGZyYW1lIGFuZCBzYXZlIHRoaXMgbmV3IGRhdGEgZnJhbWUgYXMgdW5pcXVlX3N0dWRlbnRzLg0KYGBge3J9DQojIHJlbW92ZSBkdXBsaWNhdGVkIHJvd3MsIGtlZXAgb25seSB1bmlxdWUgcm93cw0KdW5pcXVlX3N0dWRlbnRzIDwtIHN0dWRlbnRzICU+JQ0KICBkaXN0aW5jdCgpDQp1bmlxdWVfc3R1ZGVudHMNCmBgYA0KNS5Vc2UgdGhlIGR1cGxpY2F0ZWQoKSBmdW5jdGlvbiBhZ2FpbiB0byBtYWtlIGFuIG9iamVjdCBjYWxsZWQgdXBkYXRlZF9kdXBsaWNhdGVzIGFmdGVyIGRyb3BwaW5nIHRoZSBkdXBsaWNhdGVzLiBQaXBlIHRoZSByZXN1bHQgaW50byB0YWJsZSgpIHRvIHNlZSBpZiBhbnkgZHVwbGljYXRlcyByZW1haW4sIGFuZCB2aWV3IHVwZGF0ZWRfZHVwbGljYXRlcy4gQXJlIHRoZXJlIGFueSBUUlVFcyBsZWZ0Pw0KYGBge3J9DQojIGZpbmQgYW5kIGNvdW50IGR1cGxpY2F0ZWQgcm93cyBpbiB1cGRhdGVkIGRhdGEgZnJhbWUNCnVwZGF0ZWRfZHVwbGljYXRlcyA8LSB1bmlxdWVfc3R1ZGVudHMgJT4lDQogIGR1cGxpY2F0ZWQoKSAlPiUNCiAgdGFibGUoKQ0KdXBkYXRlZF9kdXBsaWNhdGVzDQpgYGANCiMgNi5TcGxpdHRpbmcgQnkgSW5kZXgNCkluIHRyeWluZyB0byBnZXQgY2xlYW4gZGF0YSwgd2Ugd2FudCB0byBtYWtlIHN1cmUgZWFjaCBjb2x1bW4gcmVwcmVzZW50cyBvbmUgdHlwZSBvZiBtZWFzdXJlbWVudC4gT2Z0ZW4sIG11bHRpcGxlIG1lYXN1cmVtZW50cyBhcmUgcmVjb3JkZWQgaW4gdGhlIHNhbWUgY29sdW1uLCBhbmQgd2Ugd2FudCB0byBzZXBhcmF0ZSB0aGVzZSBvdXQgc28gdGhhdCB3ZSBjYW4gZG8gaW5kaXZpZHVhbCBhbmFseXNpcyBvbiBlYWNoIHZhcmlhYmxlLg0KDQpMZXTigJlzIHNheSB3ZSBoYXZlIGEgY29sdW1uIOKAnGJpcnRoZGF54oCdIHdpdGggZGF0YSBmb3JtYXR0ZWQgaW4gTU1ERFlZWVkgZm9ybWF0LiBJbiBvdGhlciB3b3Jkcywg4oCcMTEwMTE5OTPigJ0gcmVwcmVzZW50cyBhIGJpcnRoZGF5IG9mIE5vdmVtYmVyIDEsIDE5OTMuIFdlIHdhbnQgdG8gc3BsaXQgdGhpcyBkYXRhIGludG8gZGF5LCBtb250aCwgYW5kIHllYXIgc28gdGhhdCB3ZSBjYW4gdXNlIHRoZXNlIGNvbHVtbnMgYXMgc2VwYXJhdGUgZmVhdHVyZXMuDQoNCkluIHRoaXMgY2FzZSwgd2Uga25vdyB0aGUgZXhhY3Qgc3RydWN0dXJlIG9mIHRoZXNlIHN0cmluZ3MuIFRoZSBmaXJzdCB0d28gY2hhcmFjdGVycyB3aWxsIGFsd2F5cyBjb3JyZXNwb25kIHRvIHRoZSBtb250aCwgdGhlIHNlY29uZCB0d28gdG8gdGhlIGRheSwgYW5kIHRoZSByZXN0IG9mIHRoZSBzdHJpbmcgd2lsbCBhbHdheXMgY29ycmVzcG9uZCB0byB5ZWFyLiBXZSBjYW4gZWFzaWx5IGJyZWFrIHRoZSBkYXRhIGludG8gdGhyZWUgc2VwYXJhdGUgY29sdW1ucyBieSBzcGxpdHRpbmcgdGhlIHN0cmluZ3MgaW50byBzdWJzdHJpbmdzIHVzaW5nIHN0cl9zdWIoKSwgYSBoZWxwZnVsIGZ1bmN0aW9uIGZyb20gdGhlIHN0cmluZ3IgcGFja2FnZToNCg0KLy8gQ3JlYXRlIHRoZSAnbW9udGgnIGNvbHVtbg0KZGYgJT4lDQogIG11dGF0ZShtb250aCA9IHN0cl9zdWIoYmlydGhkYXksMSwyKSkNCg0KLy8gQ3JlYXRlIHRoZSAnZGF5JyBjb2x1bW4NCmRmICU+JQ0KICBtdXRhdGUoZGF5ID0gc3RyX3N1YihiaXJ0aGRheSwzLDQpKQ0KDQovLyBDcmVhdGUgdGhlICd5ZWFyJyBjb2x1bW4NCmRmICU+JQ0KICBtdXRhdGUoeWVhciA9IHN0cl9zdWIoYmlydGhkYXksNSkpDQogIA0KMS5UaGUgZmlyc3QgY29tbWFuZCB0YWtlcyB0aGUgY2hhcmFjdGVycyBzdGFydGluZyBhdCBpbmRleCAxIGFuZCBlbmRpbmcgYXQgaW5kZXggMiBvZiBlYWNoIHZhbHVlIGluIHRoZSBiaXJ0aGRheSBjb2x1bW4gYW5kIHB1dHMgaXQgaW50byBhIG1vbnRoIGNvbHVtbi4NCg0KMi5UaGUgc2Vjb25kIGNvbW1hbmQgdGFrZXMgdGhlIGNoYXJhY3RlcnMgc3RhcnRpbmcgYXQgaW5kZXggMyBhbmQgZW5kaW5nIGF0IGluZGV4IDQgb2YgZWFjaCB2YWx1ZSBpbiB0aGUgYmlydGhkYXkgY29sdW1uIGFuZCBwdXRzIGl0IGludG8gYSBkYXkgY29sdW1uLg0KDQozLlRoZSB0aGlyZCBjb21tYW5kIHRha2VzIHRoZSBjaGFyYWN0ZXJzIHN0YXJ0aW5nIGF0IGluZGV4IDUgYW5kIGVuZGluZyBhdCB0aGUgZW5kIG9mIHRoZSB2YWx1ZSBpbiB0aGUgYmlydGhkYXkgY29sdW1uIGFuZCBwdXRzIGl0IGludG8gYSB5ZWFyIGNvbHVtbi4NCg0KVGhpcyB3b3VsZCB0cmFuc2Zvcm0gYSB0YWJsZSBsaWtlOg0KDQppZAkgIGJpcnRoZGF5DQoNCjEwMTEJ4oCcMTIyNDE5ODnigJ0NCg0KMTExMgnigJwxMDMxMTk2NuKAnQ0KDQoxMTEzCeKAnDAxMDUyMDEx4oCdDQoNCmludG8gYSB0YWJsZSBsaWtlOg0KDQppZAkgIGJpcnRoZGF5CSAgbW9udGgJZGF5CSAgeWVhcg0KDQoxMDExCeKAnDEyMjQxOTg54oCdCeKAnDEy4oCdCeKAnDI04oCdCeKAnDE5ODnigJ0NCg0KMTExMgnigJwxMDMxMTk2NuKAnQnigJwxMOKAnQnigJwzMeKAnQnigJwxOTY24oCdDQoNCjExMTMJ4oCcMDEwNTIwMTHigJ0J4oCcMDHigJ0J4oCcMDXigJ0J4oCcMjAxMeKAnQ0KDQpXZSB3aWxsIHByYWN0aWNlIGNoYW5naW5nIHN0cmluZyBjb2x1bW5zIGludG8gbnVtZXJpY2FsIGNvbHVtbnMgKGxpa2UgY29udmVydGluZyAiMTAiIHRvIDEwKSBpbiBhIGZ1dHVyZSBleGVyY2lzZS4NCg0KIyMgSW5zdHJ1Y3Rpb25zDQoxLlByaW50IG91dCB0aGUgY29sdW1ucyBvZiB0aGUgc3R1ZGVudHMgZGF0YSBmcmFtZS4NCmBgYHtyfQ0KIyBwcmludCBjb2x1bW5zIG9mIHN0dWRlbnRzDQpwcmludChjb2xuYW1lcyhzdHVkZW50cykpDQpgYGANCjIuVGhlIGNvbHVtbiBnZW5kZXJfYWdlIHNvdW5kcyBsaWtlIGl0IGNvbnRhaW5zIGJvdGggZ2VuZGVyIGFuZCBhZ2UhDQoNClZpZXcgdGhlIGhlYWQoKSBvZiBzdHVkZW50cyB0byBzZWUgd2hhdCBraW5kIG9mIGRhdGEgZ2VuZGVyX2FnZSBjb250YWlucy4NCmBgYHtyfQ0KIyB2aWV3IGhlYWQgb2Ygc3R1ZGVudHMNCmhlYWQoc3R1ZGVudHMpDQpgYGANCjMuSXQgbG9va3MgbGlrZSB0aGUgZmlyc3QgY2hhcmFjdGVyIG9mIHRoZSB2YWx1ZXMgaW4gZ2VuZGVyX2FnZSBjb250YWlucyB0aGUgZ2VuZGVyLCB3aGlsZSB0aGUgcmVzdCBvZiB0aGUgc3RyaW5nIGNvbnRhaW5zIHRoZSBhZ2UuIExldOKAmXMgc2VwYXJhdGUgb3V0IHRoZSBnZW5kZXIgZGF0YSBpbnRvIGEgbmV3IGNvbHVtbiBjYWxsZWQgZ2VuZGVyLiBTYXZlIHRoZSByZXN1bHQgdG8gc3R1ZGVudHMsIGFuZCB2aWV3IHRoZSBoZWFkKCkuDQpgYGB7cn0NCiMgYWRkIGdlbmRlciBhbmQgYWdlIGNvbHVtbnMNCmxpYnJhcnkoc3RyaW5ncikNCnN0dWRlbnRzIDwtIHN0dWRlbnRzICU+JQ0KICBtdXRhdGUoZ2VuZGVyID0gc3RyX3N1YihnZW5kZXJfYWdlLCAxLCAxKSkNCmhlYWQoc3R1ZGVudHMpDQpgYGANCjQuTm93LCBzZXBhcmF0ZSBvdXQgdGhlIGFnZSBkYXRhIGludG8gYSBuZXcgY29sdW1uIGNhbGxlZCBhZ2UuIFNhdmUgdGhlIHVwZGF0ZWQgZGF0YSBmcmFtZSB0byBzdHVkZW50cywgYW5kIHZpZXcgdGhlIGhlYWQoKS4NCmBgYHtyfQ0KIyBhZGQgZ2VuZGVyIGFuZCBhZ2UgY29sdW1ucw0Kc3R1ZGVudHMgPC0gc3R1ZGVudHMgJT4lDQogIG11dGF0ZShhZ2UgPSBzdHJfc3ViKGdlbmRlcl9hZ2UsIDIpKQ0KaGVhZChzdHVkZW50cykNCmBgYA0KNS5Ob3csIHdlIGRvbuKAmXQgbmVlZCB0aGF0IGdlbmRlcl9hZ2UgY29sdW1uIGFueW1vcmUuIERyb3AgZ2VuZGVyX2FnZSBmcm9tIHN0dWRlbnRzLCBhbmQgc2F2ZSB0aGUgcmVzdWx0IHRvIHN0dWRlbnRzLiBWaWV3IHRoZSBoZWFkKCkgb2Ygc3R1ZGVudHMuDQpgYGB7cn0NCiMgZHJvcCBnZW5kZXJfYWdlIGNvbHVtbg0Kc3R1ZGVudHMgPC0gc3R1ZGVudHMgJT4lDQogIHNlbGVjdCgtZ2VuZGVyX2FnZSkNCmhlYWQoc3R1ZGVudHMpDQpgYGANCiMgNy5TcGxpdHRpbmcgQnkgQ2hhcmFjdGVyDQpMZXTigJlzIHNheSB3ZSBoYXZlIGEgY29sdW1uIGNhbGxlZCAidHlwZSIgd2l0aCBkYXRhIGVudHJpZXMgaW4gdGhlIGZvcm1hdCAiYWRtaW5fVVMiIG9yICJ1c2VyX0tlbnlhIiwgYXMgc2hvd24gaW4gdGhlIHRhYmxlIGJlbG93Lg0KDQppZAkgIHR5cGUNCg0KMTAxMQnigJx1c2VyX0tlbnlh4oCdDQoNCjExMTIJ4oCcYWRtaW5fVVPigJ0NCg0KMTExMwnigJxtb2RlcmF0b3JfVUvigJ0NCg0KSnVzdCBsaWtlIHdlIHNhdyBiZWZvcmUsIHRoaXMgY29sdW1uIGFjdHVhbGx5IGNvbnRhaW5zIHR3byB0eXBlcyBvZiBkYXRhLiBPbmUgc2VlbXMgdG8gYmUgdGhlIHVzZXIgdHlwZSAod2l0aCB2YWx1ZXMgbGlrZSDigJxhZG1pbuKAnSBvciDigJx1c2Vy4oCdKSBhbmQgb25lIHNlZW1zIHRvIGJlIHRoZSBjb3VudHJ5IHRoaXMgdXNlciBpcyBpbiAod2l0aCB2YWx1ZXMgbGlrZSDigJxVU+KAnSBvciDigJxLZW55YeKAnSkuDQoNCldlIGNhbiBubyBsb25nZXIganVzdCBzcGxpdCBhbG9uZyB0aGUgZmlyc3QgNCBjaGFyYWN0ZXJzIGJlY2F1c2UgYWRtaW4gYW5kIHVzZXIgYXJlIG9mIGRpZmZlcmVudCBsZW5ndGhzLiBJbnN0ZWFkLCB3ZSBrbm93IHRoYXQgd2Ugd2FudCB0byBzcGxpdCBhbG9uZyB0aGUgIl8iLiBXZSBjYW4gdGh1cyB1c2UgdGhlIHRpZHlyIGZ1bmN0aW9uIHNlcGFyYXRlKCkgdG8gc3BsaXQgdGhpcyBjb2x1bW4gaW50byB0d28sIHNlcGFyYXRlIGNvbHVtbnM6DQoiDQpDcmVhdGUgdGhlICd1c2VyX3R5cGUnIGFuZCAnY291bnRyeScgY29sdW1ucw0KLy9kZiAlPiUNCi8vICBzZXBhcmF0ZSh0eXBlLGMoJ3VzZXJfdHlwZScsJ2NvdW50cnknKSwnXycpDQoiDQoNCjEudHlwZSBpcyB0aGUgY29sdW1uIHRvIHNwbGl0DQoNCjIuYygndXNlcl90eXBlJywnY291bnRyeScpIGlzIGEgdmVjdG9yIHdpdGggdGhlIG5hbWVzIG9mIHRoZSB0d28gbmV3IGNvbHVtbnMNCg0KMy4nXycgaXMgdGhlIGNoYXJhY3RlciB0byBzcGxpdCBvbg0KDQpUaGlzIHdvdWxkIHRyYW5zZm9ybSB0aGUgdGFibGUgYWJvdmUgaW50byBhIHRhYmxlIGxpa2U6DQoNCmlkCSAgdXNlcl90eXBlCSAgIGNvdW50cnkNCg0KMTAxMQnigJx1c2Vy4oCdCSAgICAg4oCcS2VueWHigJ0NCg0KMTExMgnigJxhZG1pbuKAnQkgICAgIOKAnFVT4oCdDQoNCjExMTMJ4oCcbW9kZXJhdG9y4oCdCSDigJxVS+KAnQ0KDQojIyBJbnN0cnVjdGlvbnMNCjEuVmlldyB0aGUgaGVhZCgpIG9mIHN0dWRlbnRzLiBOb3RpY2UgdGhhdCB0aGUgc3R1ZGVudHPigJkgbmFtZXMgYXJlIHN0b3JlZCBpbiBhIGNvbHVtbiBjYWxsZWQgZnVsbF9uYW1lLg0KYGBge3J9DQojIHZpZXcgdGhlIGhlYWQgb2Ygc3R1ZGVudHMNCmhlYWQoc3R1ZGVudHMpDQpgYGANCjIuU2VwYXJhdGUgdGhlIGZ1bGxfbmFtZSBjb2x1bW4gaW50byB0d28gbmV3IGNvbHVtbnMsIGZpcnN0X25hbWUgYW5kIGxhc3RfbmFtZSwgYnkgc3BsaXR0aW5nIG9uIHRoZSAnICcgY2hhcmFjdGVyIC4NCg0KUHJvdmlkZSBhcyBhbiBleHRyYSBhcmd1bWVudCB0byB0aGUgc2VwYXJhdGUoKSBmdW5jdGlvbiBleHRyYSA9J21lcmdlJy4gVGhpcyB3aWxsIGVuc3VyZSB0aGF0IG1pZGRsZSBuYW1lcyBvciB0d28td29yZCBsYXN0IG5hbWVzIHdpbGwgYWxsIGVuZCB1cCBpbiB0aGUgbGFzdF9uYW1lIGNvbHVtbi4NCg0KU2F2ZSB0aGUgcmVzdWx0IHRvIHN0dWRlbnRzLCBhbmQgdmlldyB0aGUgaGVhZCgpLg0KYGBge3J9DQojIHNlcGFyYXRlIHRoZSBmdWxsX25hbWUgY29sdW1uDQpzdHVkZW50cyA8LSBzdHVkZW50cyAlPiUNCiAgc2VwYXJhdGUoJ2Z1bGxfbmFtZScsIGMoJ2ZpcnN0X25hbWUnLCAnbGFzdF9uYW1lJyksICcgJywgZXh0cmEgPSdtZXJnZScpDQpoZWFkKHN0dWRlbnRzKQ0KYGBgDQojIDguTG9va2luZyBhdCBEYXRhIFR5cGVzDQpFYWNoIGNvbHVtbiBvZiBhIGRhdGEgZnJhbWUgY2FuIGhvbGQgaXRlbXMgb2YgdGhlIHNhbWUgZGF0YSB0eXBlLiBUaGUgZGF0YSB0eXBlcyB0aGF0IFIgdXNlcyBhcmU6IGNoYXJhY3RlciwgbnVtZXJpYyAocmVhbCBvciBkZWNpbWFsKSwgaW50ZWdlciwgbG9naWNhbCwgb3IgY29tcGxleC4gT2Z0ZW4sIHdlIHdhbnQgdG8gY29udmVydCBiZXR3ZWVuIHR5cGVzIHNvIHRoYXQgd2UgY2FuIGRvIGJldHRlciBhbmFseXNpcy4gSWYgYSBudW1lcmljYWwgY2F0ZWdvcnkgbGlrZSAibnVtX3VzZXJzIiBpcyBzdG9yZWQgYXMgYSB2ZWN0b3Igb2YgY2hhcmFjdGVycyBpbnN0ZWFkIG9mIG51bWVyaWNzLCBmb3IgZXhhbXBsZSwgaXQgbWFrZXMgaXQgbW9yZSBkaWZmaWN1bHQgdG8gZG8gc29tZXRoaW5nIGxpa2UgbWFrZSBhIGxpbmUgZ3JhcGggb2YgdXNlcnMgb3ZlciB0aW1lLg0KDQpUbyBzZWUgdGhlIHR5cGVzIG9mIGVhY2ggY29sdW1uIG9mIGEgZGF0YSBmcmFtZSwgd2UgY2FuIHVzZToNCg0Kc3RyKGRmKQ0KDQpzdHIoKSBkaXNwbGF5cyB0aGUgaW50ZXJuYWwgc3RydWN0dXJlIG9mIGFuIFIgb2JqZWN0LiBDYWxsaW5nIHN0cigpIHdpdGggYSBkYXRhIGZyYW1lIGFzIGFuIGFyZ3VtZW50IHdpbGwgcmV0dXJuIGEgdmFyaWV0eSBvZiBpbmZvcm1hdGlvbiwgaW5jbHVkaW5nIHRoZSBkYXRhIHR5cGVzLiBGb3IgYSBkYXRhIGZyYW1lIGxpa2UgdGhpczoNCg0KaXRlbQkgICAgICAgICBwcmljZQkgICBjYWxvcmllcw0KDQrigJxiYW5hbmHigJ0JICAgICDigJwkMeKAnQkgICAgIDEwNQ0KDQrigJxhcHBsZeKAnQkgICAgICAg4oCcJDAuNzXigJ0JIDk1DQoNCuKAnHBlYWNo4oCdCSAgICAgICDigJwkM+KAnQkgICAgIDU1DQoNCuKAnGNsZW1lbnRpbmXigJ0JIOKAnCQyLjXigJ0JICAgMzUNCg0KdGhlIGRhdGEgdHlwZXMgd291bGQgYmU6DQoNCi8vICAjPiAkIGl0ZW06ICAgICAgICBjaHINCg0KLy8gICM+ICQgcHJpY2U6ICAgICAgIGNocg0KDQovLyAgIz4gJCBjYWxvcmllczogICAgbnVtDQoNCldlIGNhbiBzZWUgdGhhdCB0aGUgcHJpY2UgY29sdW1uIGlzIG1hZGUgdXAgb2YgY2hhcmFjdGVycywgd2hpY2ggd2lsbCBwcm9iYWJseSBtYWtlIG91ciBhbmFseXNpcyBvZiBwcmljZSBtb3JlIGRpZmZpY3VsdC4gV2XigJlsbCBsb29rIGF0IGhvdyB0byBjb252ZXJ0IGNvbHVtbnMgaW50byBudW1lcmljIHZhbHVlcyBpbiB0aGUgbmV4dCBmZXcgZXhlcmNpc2VzLg0KDQojIyBJbnN0cnVjdGlvbnMNCjEuTGV04oCZcyBpbnNwZWN0IHRoZSBkYXRhIHR5cGVzIGluIHRoZSBzdHVkZW50cyB0YWJsZS4NCg0KUHJpbnQgb3V0IHRoZSBzdHJ1Y3R1cmUgb2Ygc3R1ZGVudHMuDQpgYGB7cn0NCiMgcHJpbnQgc3RydWN0dXJlIG9mIHN0dWRlbnRzDQpzdHIoc3R1ZGVudHMpDQpgYGANCjIuSWYgd2Ugd2FudGVkIHRvIG1ha2UgYSBzY2F0dGVycGxvdCBvZiBhZ2UgdnMgYXZlcmFnZSBleGFtIHNjb3JlLCB3b3VsZCB3ZSBiZSBhYmxlIHRvIGRvIGl0IHdpdGggdGhpcyB0eXBlIG9mIGRhdGE/DQoNClBhc3RlIHRoZSBmb2xsb3dpbmcgY29kZSBpbiB0aGUgbGFzdCBjb2RlIGJsb2NrIHRvIHRyeSBhbmQgcHJpbnQgb3V0IHRoZSBtZWFuIG9mIHRoZSBzY29yZSBjb2x1bW4gb2Ygc3R1ZGVudHMuDQoNCiIgc3R1ZGVudHMgJT4lIA0KICBzdW1tYXJpc2UobWVhbl9zY29yZSA9IG1lYW4oc2NvcmUpKSAiDQoNCldoYXQgd2FybmluZyBkbyB5b3Ugc2VlPw0KYGBge3J9DQojIG1lYW4gb2YgYWdlIGNvbHVtbg0Kc3R1ZGVudHMgJT4lIA0KICBzdW1tYXJpc2UobWVhbl9zY29yZSA9IG1lYW4oc2NvcmUpKQ0KYGBgDQoNCiMgOS5TdHJpbmcgUGFyc2luZw0KU29tZXRpbWVzIHdlIG5lZWQgdG8gbW9kaWZ5IHN0cmluZ3MgaW4gb3VyIGRhdGEgZnJhbWVzIHRvIGhlbHAgdXMgdHJhbnNmb3JtIHRoZW0gaW50byBtb3JlIG1lYW5pbmdmdWwgbWV0cmljcy4gRm9yIGV4YW1wbGUsIGluIG91ciBmcnVpdHMgdGFibGUgZnJvbSBiZWZvcmU6DQoNCml0ZW0JICAgICAgICAgcHJpY2UJICBjYWxvcmllcw0KDQrigJxiYW5hbmHigJ0JICAgICDigJwkMeKAnQkgICAgMTA1DQoNCuKAnGFwcGxl4oCdCSAgICAgICDigJwkMC43NeKAnQk5NQ0KDQrigJxwZWFjaOKAnQkgICAgICAg4oCcJDPigJ0JICAgIDU1DQoNCuKAnHBlYWNo4oCdCSAgICAgICDigJwkNOKAnQkgICAgNTUNCg0K4oCcY2xlbWVudGluZeKAnQkg4oCcJDIuNeKAnQkgIDM1DQoNCldlIGNhbiBzZWUgdGhhdCB0aGUgJ3ByaWNlJyBjb2x1bW4gaXMgYWN0dWFsbHkgY29tcG9zZWQgb2YgY2hhcmFjdGVyIHN0cmluZ3MgcmVwcmVzZW50aW5nIGRvbGxhciBhbW91bnRzLiBUaGlzIGNvbHVtbiBjb3VsZCBiZSBtdWNoIGJldHRlciByZXByZXNlbnRlZCBhcyBudW1lcmljLCBzbyB0aGF0IHdlIGNvdWxkIHRha2UgdGhlIG1lYW4sIGNhbGN1bGF0ZSBvdGhlciBhZ2dyZWdhdGUgc3RhdGlzdGljcywgb3IgY29tcGFyZSBkaWZmZXJlbnQgZnJ1aXRzIHRvIG9uZSBhbm90aGVyIGluIHRlcm1zIG9mIHByaWNlLg0KDQpGaXJzdCwgd2UgY2FuIHVzZSBhIHJlZ3VsYXIgZXhwcmVzc2lvbiwgYSBzZXF1ZW5jZSBvZiBjaGFyYWN0ZXJzIHRoYXQgZGVzY3JpYmUgYSBwYXR0ZXJuIG9mIHRleHQgdG8gYmUgbWF0Y2hlZCwgdG8gcmVtb3ZlIGFsbCBvZiB0aGUgZG9sbGFyIHNpZ25zLiBUaGUgYmFzZSBSIGZ1bmN0aW9uIGdzdWIoKSB3aWxsIHJlbW92ZSB0aGUgJCBmcm9tIHRoZSBwcmljZSBjb2x1bW4sIHJlcGxhY2luZyB0aGUgc3ltYm9sIHdpdGggYW4gZW1wdHkgc3RyaW5nICcnOg0KDQoiZnJ1aXQgJT4lDQogIG11dGF0ZShwcmljZT1nc3ViKCdcXCQnLCcnLHByaWNlKSkiDQoNClRoZW4sIHdlIGNhbiB1c2UgdGhlIGJhc2UgUiBmdW5jdGlvbiBhcy5udW1lcmljKCkgdG8gY29udmVydCBjaGFyYWN0ZXIgc3RyaW5ncyBjb250YWluaW5nIG51bWVyaWNhbCB2YWx1ZXMgdG8gbnVtZXJpYzoNCg0KImZydWl0ICU+JQ0KICBtdXRhdGUocHJpY2UgPSBhcy5udW1lcmljKHByaWNlKSkiDQoNCk5vdywgd2UgaGF2ZSBhIGRhdGEgZnJhbWUgdGhhdCBsb29rcyBsaWtlOg0KaXRlbQkgICAgICAgIHByaWNlCWNhbG9yaWVzDQoNCuKAnGJhbmFuYeKAnQkgICAgMQkgICAgMTA1DQoNCuKAnGFwcGxl4oCdCSAgICAgIDAuNzUJOTUNCg0K4oCccGVhY2jigJ0JICAgICAgMwkgICAgNTUNCg0K4oCccGVhY2jigJ0JICAgICAgNAkgICAgNTUNCg0K4oCcY2xlbWVudGluZeKAnQkyLjUJICAzNQ0KDQoNCiMjIEluc3RydWN0aW9ucw0KMS5XZSBzYXcgaW4gdGhlIGxhc3QgZXhlcmNpc2UgdGhhdCBmaW5kaW5nIHRoZSBtZWFuIG9mIHRoZSBzY29yZSBjb2x1bW4gaXMgaGFyZCB0byBkbyB3aGVuIHRoZSBkYXRhIGlzIHN0b3JlZCBhcyBjaGFyYWN0ZXJzIGFuZCBub3QgbnVtYmVycy4NCg0KVmlldyB0aGUgaGVhZCgpIG9mIHN0dWRlbnRzIHRvIHRha2UgYSBsb29rIGF0IHRoZSB2YWx1ZXMgaW4gdGhlIHNjb3JlIGNvbHVtbi4NCmBgYHtyfQ0KIyB2aWV3IGhlYWQgb2Ygc3R1ZGVudHMNCmhlYWQoc3R1ZGVudHMpDQpgYGANCjIuUmVtb3ZlIHRoZSAnJScgc3ltYm9sIGZyb20gdGhlIHNjb3JlIGNvbHVtbiwgYW5kIHNhdmUgdGhlIHJlc3VsdGluZyBkYXRhIGZyYW1lIHRvIHN0dWRlbnRzLiBWaWV3IHN0dWRlbnRzLg0KYGBge3J9DQojIHJlbW92ZSAlIGZyb20gc2NvcmUgY29sdW1uDQpzdHVkZW50cyA8LSBzdHVkZW50cyAlPiUNCiAgbXV0YXRlKHNjb3JlID0gZ3N1YignJScsICcnLHNjb3JlKSkNCnN0dWRlbnRzDQpgYGANCjMuQ29udmVydCB0aGUgc2NvcmUgY29sdW1uIHRvIGEgbnVtZXJpY2FsIHR5cGUgdXNpbmcgdGhlIGFzLm51bWVyaWMoKSBmdW5jdGlvbi4gU2F2ZSB0aGlzIG5ldyBkYXRhIGZyYW1lIHRvIHN0dWRlbnRzLCBhbmQgdmlldyANCmBgYHtyfQ0KIyBjaGFuZ2Ugc2NvcmUgY29sdW1uIHRvIG51bWVyaWMNCnN0dWRlbnRzIDwtIHN0dWRlbnRzICU+JQ0KICBtdXRhdGUoc2NvcmUgPSBhcy5udW1lcmljKHNjb3JlKSkNCnN0dWRlbnRzDQpgYGANCiMgMTAuUmV2aWV3DQpHcmVhdCEgV2UgaGF2ZSBsb29rZWQgYXQgYSBudW1iZXIgb2YgZGlmZmVyZW50IG1ldGhvZHMgd2UgbWF5IHVzZSB0byBnZXQgZGF0YSBpbnRvIHRoZSBmb3JtYXQgd2Ugd2FudCBmb3IgYW5hbHlzaXMuDQoNClNwZWNpZmljYWxseSwgd2UgaGF2ZSBjb3ZlcmVkOg0KDQoxLmRpYWdub3NpbmcgdGhlIOKAnHRpZGluZXNz4oCdIG9mIGRhdGENCg0KMi5jb21iaW5pbmcgbXVsdGlwbGUgZmlsZXMNCg0KMy5yZXNoYXBpbmcgZGF0YQ0KDQo0LmNoYW5naW5nIHRoZSB0eXBlcyBvZiB2YWx1ZXMNCg0KNS5tYW5pcHVsYXRpbmcgc3RyaW5ncyB0byByZXByZXNlbnQgZGF0YSBiZXR0ZXINCg0KWW91IGNhbiB1c2UgdGhlc2UgbWV0aG9kcyB0byB0cmFuc2Zvcm0geW91ciBkYXRhc2V0cyB0byBiZSBjbGVhbiBhbmQgZWFzeSB0byB3b3JrIHdpdGghDQoNCiMjIEluc3RydWN0aW9ucw0KMS5UaGUgc3R1ZGVudHMgZGF0YSBmcmFtZSBpcyBuZWFybHkgY2xlYW5lZCBhbmQgcmVhZHkgZm9yIGFuYWx5c2lzISBUaGVyZeKAmXMgb25lIG1vcmUgY2hhbmdlIHRoYXQgY2FuIGJlIG1hZGUgdG8gdGhlIGFnZXMgb2YgdGhlIHN0dWRlbnRzIHRvIGhlbHAgZGVzY3JpYmUgYW5kIHZpc3VhbGl6ZSB0aGUgZGF0YS4gV2hhdCBjb3VsZCB0aGF0IGNoYW5nZSBiZT8gV2hhdCBpcyB0aGUgaWRlYWwgZGF0YSB0eXBlIGZvciB0aGUgYWdlcyBjb2x1bW4/DQoNCk1ha2UgdGhlIGNoYW5nZSB0byBhZ2UgYW5kIHNhdmUgdGhlIHJlc3VsdGluZyBkYXRhIGZyYW1lIHRvIHN0dWRlbnRzLg0KYGBge3J9DQojIHVwZGF0ZSBhZ2UgY29sdW1uDQpzdHVkZW50cyA8LSBzdHVkZW50cyAlPiUNCiAgbXV0YXRlKGFnZSA9IGFzLm51bWVyaWMoYWdlKSk7DQpzdHVkZW50cw0KYGBgDQo=