Chap 3. Basic R for data analysis

1 A good organisation of your project for data analysis

Fig: Workflow and directory preparation for each project

R project` Directory & structure :

  • Favors good organisation
  • Allows git / version control
  • Easier to share - allows others to have the same organisation
  • Reproducibility

Example from the Authoring scientific publications with R Markdown:

Please make those directories if they do not exist yet, in your project

Optional :

  • bin : for external code or binaries (eg. a compiled program) - (likely not under version control)
  • doc : text documents associated to the project

1.1 Exercise: Getting organized to prepare data analysis

2 First steps: preparing our data before data analysis

  • We will use the tidyverse metaverse (set of package what work well together) to manipulate data.

  • We will also try to show you the relation between baseR and dplyr (a package for data manipulation in tidyverse) to explain some basic programming concepts.

2.1 Loading required packages

By convention they are loaded at the beginning of a script or document. (However, it works well to load them when they are needed)

Unhide to see solution

library(here)
library(tidyverse)

2.2 Reading our data (our data is contained in a dataframe)

Data frames (tabular format - similar to spreadsheets) - columns : variables (including identifiant) - raws : observations

human_data <- 
  readr::read_csv2(
    here::here("data", "2024-09-25_INIKA_SAMPLING_ANALYSIS_HUMAN.csv")
    )

We need to check:

  1. If the data has been loaded correctly
  2. If the types of the data in each columns as recognized correctly

To look at the table graphically:

View(human_data)

You are unlucky - you have to learn analyzing a huge table. This is not easiest to start with. Lets proceed step by step. In the following sections, we will transform the raw data to a dataset so it becomes be easier to work with.

Note: I am writting the course at the same time as I discover the data. Somethings might be done in different order, but I want you to see how I process the data, and that well, even if I am more experienced than you, I do not know all. So I struggle and find information to I can learn and go forwards.

2.2.1 Exercise


2.3 Exploring the structure of the data

# we use tidyverse so better to stay in the same system
glimpse(human_data)

# but we could have used
# str(human_data) 

2.3.1 Types of data in the columns

It seems to be something for dates, but I will use google to find out what it is exacly: what is dttm R type. I see R for data science page - it seems to be a reliable source of information. I open the webpage then I search ddtm in this web page and I see that it is a date-time type used by tibbles. I know tibles are a name used to call a special type of dataframes (tibble package in tidyverse). We do not need to bother with further information for now.

typeof(human_data)
class(human_data)

Types of the data in columns and types of objects are different things.

I do not change the types that need to be changed, right now. This will be easier to do later on. The important thing is that the data appears to have been read correctly. We will make some checks to be sure.

2.4 Data wrangling and tyding

2.4.1 Renaming columns

I see that there are some spaces in the column names. Column names with spaces are more difficult to work with (its not impossible, but its not practical).

There are also other special characters ? / that will make work the work with the data difficult. I will also remove those.

I will change the column names to remove the spaces. I will do that step by step, which will allow us to verify what we are doing at each step.

  1. I create an object that contains the column names in an object
  2. I create a new object with new column names (spaces replaced by _), and then replacing the other special characters by _
  3. I replace the column names in the table by the new column names.

At each step we will check what we have done.

# step 1
original_columns <- colnames(human_data)
original_columns

# step 2
# replacing spaces 
new_columns <- str_replace_all(original_columns, " ", "_")
new_columns

# replacing special characters 
new_columns <- str_replace_all(new_columns, "[?/,;.*()-]", "_")
new_columns


# removing _ at the beginning and end of column names (should not start with _)
new_columns <- str_remove_all(new_columns, "(^_*)|(_*$)")
new_columns

# this can also be done, still step by step using pipe ... like that
# space is replaced by _
# a series of , or ; or . or * or ( or )

new_columns <- 
  colnames(human_data) %>%
  str_replace_all(" ", "_") %>%
  str_replace_all( "[?/,;.*()-]", "_") %>%
  str_remove_all("(^_*)|(_*$)")

new_columns

There is detailed information here on how to use regular expression with stringr package functions

Now that we are sure the new column names will be easier to use, we can replace the column names in the table. We can still readjust those ones later on if needed.

# Step 3: Replacing the column names
colnames(human_data) <- new_columns
colnames(human_data)

2.4.2 Selecting columns to reduce the size of the table we will work with

Now we need to find columns with redundant information that we will not need for the analysis. This will make the data frame easier to work with.

we can do that by column names:

glimpse(human_data)

human_data %>% 
  select(INIKA_OH_TZ_ID, Age__yrs, Gender) %>%
  View(.)

NA represents missing value (its kind of a neutral filling of the cell)

or by column numbers (starting from 1), it can be easier than to type

colnames(human_data)

human_data %>% 
  select(3,4,5) %>%
  head(.)

There are too many columns in the data, some with redundant data (eg. the different Gender columns, and many others).

Here, For demonstration purposes, I select a subset of the columns (this will be enough)

Tricks: The tab is helpful to help you write the end of column names (basically the questions that are detailed as 0 or 1 where there is text column do not need to be taken in.)

human_data_selection <- 
  human_data %>%
  select(INIKA_OH_TZ_ID, Age__yrs, Gender, Enter_a_date, Region, District, 
         Specify_if_other_district, Sample, Season, Origin_of_sample, 
         Which_class_grade_are_you, 
         Who_is_your_caretaker, 
         If_others__mention, 
         What_is_your_occupation_and_or_of_your_caretaker, 
         Have_you_ever_heard_about_AMR, If_yes__how_did_you_get_this_information, 
         Have_you_or_your_children_used_any_antibiotics_at_any_time, 
         If_yes__where_did_you_get_these_drugs_from,
         If_it_was_drug_sellers_or_pharmacy__did_you_have_a_prescription_from_the_doctor_prescriber,
         GPS_coordinates_latitude, GPS_coordinates_longitude) 

human_data_selection %>% View()

2.4.3 Some data verification and removing one emtpy column

glimpse(human_data_selection)

I see that Specify_if_other_district is of type logical (TRUE or FALSE) lgl this is unexpected, as if something had been registered in here, there should be text (character type)

I suspect that nothing has been registered. I will check that.

  • This is a way to look at the content of the column only
human_data_selection$Specify_if_other_district
  • I can see if there is something that is not NA by making a specific subset of the column, and then counting the number of elements in that subset
# create a logical vector : if is NOT NA wil register TRUE, otherwise if NA will put FALSE
# A logical vector allows to select values that are TRUE
# Example 

learn_test <- c(NA, "not empty", " ")
!is.na(learn_test)
test <- learn_test[!is.na(learn_test)]
test
length(test)

We do the same for the column of our dataset.

!is.na(human_data_selection$Specify_if_other_district)

test <- 
  human_data_selection$Specify_if_other_district[!is.na(human_data_selection$Specify_if_other_district)]

length(test)

Ok, this column is totally empty, we can remove it from the data set. A negative selection allows to remove columns. We need to replace the data frame with the new one (reassignment)

human_data_selection <- 
  human_data_selection %>%
  select(-Specify_if_other_district)

# verification it has been removed
colnames(human_data_selection)

2.4.4 Changing the data types of some columns

glimpse(human_data_selection)

INIKA identifier needs to be read as character (we do not want to treat it as a number but as a text)

The age (in years) could be an integer (whole number) instead of a double. We will change that

Yes/No answers can be treated as either logical (TRUE/FALSE) or as factors We can look at the different levels (the different values, here answers, taken by a variable)

NA (missing values are NOT transformed into a level by default, by default they are excluded). Type F1 for help.

Here we will treat them as a level of a factor, just you show you that you need to have critical sense, otherwise you might not detect mistakes. We will show you how to correct this mistakes.

Normally we do not use the exclude option, but I want to show you how it affects the rest. We will fix that later

# this is a way 
human_data_selection$Have_you_ever_heard_about_AMR

levels(factor(human_data_selection$Have_you_ever_heard_about_AMR, 
                  exclude = NULL))

Another way is to look at the distinct values of the column

# or this is another way using tidyverse
human_data_selection %>%
  select(Have_you_ever_heard_about_AMR) %>%
  distinct()

I want to treat those as factor. I will have to create a transformation of the tables types that account for NA values. I want to know how many and where data are missing.

First I will show you how you can change types, and check the result at the same time. After we put everything together and do it in one go, and modify the table.

mutate: transform the data (it can be types, or some calculation…)

# A way to do this
human_data_selection %>%
  mutate(INIKA_OH_TZ_ID = as.character(INIKA_OH_TZ_ID)) %>%
  glimpse()

# another way to do that
human_data_selection %>%
  mutate_at(vars(INIKA_OH_TZ_ID), as.character) %>%
  glimpse()

When there is only one column, its possible to write the name of the column like that and it will work.

human_data_selection %>%
  mutate_at("INIKA_OH_TZ_ID", as.character)  %>%
  glimpse()

As you can see, there are several solutions, pick one that you understand

You can also see that because I did not reassign the changes, then I can test the way to do things without modifying my data. When I am sure that what I do is correct, then I can reassign the changes to the original object.

If I do a mistake, well, this is still not a catastrophe, I rerun the whole code until were I made the mistake, and can correct this mistake.


Now I want to transform many columns to factor. Because I want to create a summary of the table, which will be easy to look at and count the number of observations in each category.

I can select also by column number, this allows to select slices of columns

colnames(human_data_selection)

human_data_selection %>%
  mutate_at(vars(3, 5:18), factor,  exclude = NULL)  %>%
  glimpse()

And I want to transform the age to integer (that will be useful for display in categories)

human_data_selection %>%
  mutate("Age__yrs" = as.integer(Age__yrs)) %>%
  glimpse()

Now we put together all the steps and modify the table.

human_data_selection <- 
  human_data_selection %>%
  mutate_at(vars(INIKA_OH_TZ_ID), as.character) %>%
  mutate("Age__yrs" = as.integer(Age__yrs)) %>%
  mutate_at(vars(3, 5:18), factor,  exclude = NULL) 

glimpse(human_data_selection)

2.4.5 Summary of the data : a fast overview of the data contained in the dataset

Now we can have an overview of the data content. We will count the number of values taken by factor and have some summary statistics for the numerical values.

summary(human_data_selection)

Its useful but not very nice. It can be done with dplyr … but is not as complete. I had written a function that we can use to have a formatted summary. We can use external function by sourcing the file.

2.4.5.1 Bonus - formatting the summary table and export in a file (we do not do during the course)

This is a little quirk to make the results more readable to make it visible, were we transform the summary into a fake table (emtpy lines are added)

Try to load those packages, if it does not work, you need to install them, because the function relies on those packages

# this is the path of the file 
here::here("src", "format_summary_statistics_fun.R")
# this allows to load the function contained into the file in the memory
source(here::here("src", "format_summary_statistics_fun.R"))

We see in the environment panel that the format_summary_statistics function is now available.

We can look at the code of the function like that

format_summary_statistics_fun

You can also open the file, to look at it. This is a bit to advanced for now. There is some explanation included in the file on how to use it.

format_summary_statistics_fun(human_data_selection) %>% View()

It is a bit more readable, if you had a results directory then it should be saved there as a tsv (tabulation separated values) file.

2.4.6 Filtering out unwanted values.

Oops, we see that we have registered rows that should not be there. We will filter out those data from the data set.

Another way to confirm is to look at the range of values

human_data_selection %>%
  select(Age__yrs) %>%
  range(na.rm = TRUE)

how many of those data we need to remove ? - by transforming the data type to factor, I can have a contingency table of the different values of the variable (here - age).

factor((human_data_selection$Age__yrs), exclude = NULL) %>%
  table()

We see that 87 rows should be removed from the data set.

We remove the data that we do not want by applying a filter, to only select rows from which values in a certain column contains values we want.

test <- 
  human_data_selection %>%
  filter(Age__yrs >= 12) 
  
nrow(human_data_selection)
nrow(test)

Inline_code: We filtered out 88 rows.

Oops … We filtered one too much. We removed the row where the value was missing. The choice of removing this value definitively depends on the context. Can the value be recovered from (eg. field notes ?) and then corrected HERE !. Will it influence our analyses or not ? Can it be estimated from other values ?

For now we will keep the NA value ( we are learning)

test <- 
  human_data_selection %>%
  filter(Age__yrs >= 12 | is.na(Age__yrs)) 

nrow(human_data_selection) - nrow(test)  

table(factor((test$Age__yrs), exclude = NULL))

This now correct. We can apply this filter to the data frame.

human_data_selection <- 
  human_data_selection %>%
  filter(Age__yrs >= 12 | is.na(Age__yrs)) 

We can look again at the data content:

summary(human_data_selection)

Note : you always need to check that you are doing the right thing. You can also write small dataset tests to be sure that you do the correct thing. When you have written code, it easy to rerun it when we have found our mistakes and corrected them. It is not possible to detect mistakes and correct them when we correct values on a spreadsheet directly. This is why the raw data should NOT be modified directly.

2.4.7 Selecting rows with missing data to see if we can recover them from field notes

human_data_selection %>%
  filter(is.na(Age__yrs)) %>%
  # this allows to ensure that we see all the columns in the console 
  print(width=Inf)

hum, all the data of this ID appears to be missing. We can remove this raw from the data set

2.4.7.1 Exercise : remove the row from the dataset

Remove the row containing missing value in Age from the data set.

Hint: this means that you need to keep everything that is Not missing (put ! in front of is.na command).

Do not forget to do a test, and control that is does what you want.

# this should work  to only keep those with NA
test <- 
  human_data_selection %>%
  filter(!is.na(Age__yrs))

test
# control  - I check if I find those with NA again 
test %>%
  filter(is.na(Age__yrs)) %>%
  # this allows to ensure that we see all the columns in the console 
  print(width=Inf)

# OR a trick 
is.na(test$Age__yrs)
sum(is.na(test$Age__yrs))
# TRUE has a value of 1, FALSE has a value of 0
# example 
sum(c(TRUE, FALSE, TRUE))

NB: c() function is a way to write vectors (one dimensional arrays).

2.4.8 Selecting rows that contains missing data in any column

If we have few missing data, we can go faster in our check by selecting rows that contains missing data in any column.

View(human_data_selection) 
# I see some other columns with NA (show how to do) - eg who is your caretaker


human_data_selection %>%
  filter(if_any(everything(), is.na))

Euhh … this does not work as intended … why ?

This is because I have transformed the data type of the columns to factor, and NA values are considered as a level.

When we look at factors, we see characters labels. Actually factors are encoded in R as integers. Lets try to understand this.

Here is a little test to understand how factor works.

# I create a vector character with some values
test_vector <- c("1", "3", "C", NA)
test_factor <- factor(test_vector, exclude = NULL)

test_factor
as.integer(test_factor) # its a factor - it can be coerced to a number
attributes(test_factor)

as.integer(test_vector) # its character - "C" letter cannot be coerced ("1" can be)
attributes(test_vector)

# another example 
test <- factor(2:4)
test

as.integer(test) # the levels starts at 1 ... the levels are filed in order they are populated

# test 
which(levels(test_factor) == "C")
which(levels(test_factor) == NA) # this is not the correct way to test, NA is particular

is.na(levels(test_factor))
which(is.na(levels(test_factor)))

Ok, so we need to change the data type of the columns again, to be able to see which factors contains NA.

Note: You see why its important to have critical sense. A single error at a line can induce error to your dataset. Therefore you need to check that the code you write does what you want.

To change the data types of factors, we need to revert first to characters, then we can re-change to factor and this time NA will not be included in the levels.

# changing the data types (a reminder)
# mutate if does a test - if the test is True then the type change is applied
test <- 
  human_data_selection %>%
  mutate_if(is.factor, as.character) %>%
  mutate_if(is.character, as.factor) 

glimpse(test)

levels(test$Who_is_your_caretaker)  # NA are not in levels 
unique(test$Who_is_your_caretaker)  # but the are listed in unique as missing values

# good this worked - so we can make the changes to the data frame
human_data_selection <- test

Now we can test again if we have missing values in any column.

human_data_selection %>%
  filter(if_any(everything(), is.na)) 
human_data_selection %>%
  filter(if_any(everything(), is.na)) %>%
  dim()

We have missing many values in the data set, in 402 rows over 20 columns. Lets have another look at the summary, is this distributed over all columns or are there some columns where its expected to have many NAs ?

summary(human_data_selection %>% select(-INIKA_OH_TZ_ID))

“If_others__mention”, “If_yes__how_did_you_get_this_information”, “If_yes__where_did_you_get_these_drugs_from”,
“If_it_was_drug_sellers_or_pharmacy__did_you_have_a_prescription_from_the_doctor_prescriber”

Are columns we can expect many NAs due to the question was asked, because it was a continuation of a particular case.

However, we need to inspect closer the columns “Which_class_grade_are_you”, and “Who_is_your_caretaker”

2.4.8.1 Exercise : Counting the number of NAs in each column (we do not do)

… and using it to find column with many NAs

We can have a different approach to find which columns containing many NAs, eg by counting the number of NAs in each column.

# filter columns that contain more than 10 NAs
temp <- human_data_selection %>%
  mutate(across(everything(), is.na)) %>% # this will transform the data frame to TRUE/FALSE (TRUE it its na)
  summarise_all(sum) # because TRUE is 1, FALSE is 0, we can sum to count the number of NAs

temp # where we see howm many Nas we have

human_data_selection %>%
  mutate(across(everything(), is.na)) %>%
  summarise_all(sum) %>%
  select(where(~sum(.) > 10)) %>% # now we select the columns where the sum is above 10
  colnames() # and we get the column names where there are mores than 10 NAs

# trick comment %>% and execute before to see what it does

now we have obtained the list of columns with NA that I had put in the comments (hidden in html) above.

2.4.9 Checking that IDs are unique - Distinct values usage

We need to ensure that IDs are unique. If not, there is probably a problem with the data. The data might have been registered incompletely or several times. We need also to check that if we have duplicated IDs that the information, if incompletely registered is not incompatible. This can allow to identify errors in data registration.

NB: Such errors are not uncommon

We need first to remove eventual duplicated rows, where the ID and all values in all columns are identical. If some columns are empty, this means there can have been some problems during the registering of the data (eg. sent several times and accepted by the database).

  • Distinct allows to filter out rows that are totally identical (it means identical in all columns)
dim(human_data_selection)

human_data_selection_dedup <- 
  human_data_selection %>%
  distinct() 

dim(human_data_selection_dedup)

9 rows of totally identical rows were removed

Now we need to consider rows where the ID is duplicated, but the rest of the data is not identical. Again this can be an error in data having being first partially registred, followed by a better registering, or human error (eg. using the same ID)

glimpse(human_data_selection_dedup)

Here there is something I did not recheck. I will need to use the ID to filter rows that are problematic and inspect them further. But It is not practical doing that when the IDs are of type factor (this complicate making the correspondence between actual ID and factor value). So I prefer to re-convert the ID to character.

Note: I should have thought better when I originally converted the types. The best way to do things fastg, is not always evident at first. This is normal.

We will obtain duplicated rows that are not totally identical. Grouping by the ID, we can count the number of rows per ID.

NOTE IMPORTANT > ! FOR NOW ON - human_data_selection_dedup will be the version of the data we will be working on (this allows us not to have to run everything again if we make a mistake. Then we can only run again since the creation of human_data_selection_dedup)

# changing the type of the ID to character
human_data_selection_dedup <- 
  human_data_selection_dedup %>%
  mutate_at(vars(INIKA_OH_TZ_ID), as.character) %>%
  # order - trick to use the ID as a number
  arrange(as.integer(INIKA_OH_TZ_ID))

# removing eventual duplicated rows 
duplicated_data <- 
  human_data_selection_dedup %>%
  # grouping data that have same ids 
  group_by(INIKA_OH_TZ_ID) %>%
  # This add a column of count  - the count counts BY GROUP
  mutate(count = n(), .after = "INIKA_OH_TZ_ID") %>%
  # This filter out the rows that are not unique
  filter(count > 1) 
  
duplicated_data  %>% View()

2.4.9.1 Exercise: Understanding group_by

We prepare the common part for the test. As done abouve, but before the group by.

test <- 
  human_data_selection_dedup %>%
  mutate_at(vars(INIKA_OH_TZ_ID), as.character) %>%
  arrange(as.integer(INIKA_OH_TZ_ID)) 

Group by gender

test %>%
  ungroup() %>%
  group_by(Gender) %>%
  # This add a column of count 
  mutate(count = n(), .after = "INIKA_OH_TZ_ID") 

The count is now actually identical to the number of males and females. Because we added the column count as we did, this count appears in each column.

One way (functional but not the best) to create a contingency table :

test %>%
  ungroup() %>%
  group_by(Gender) %>%
  # This add a column of count 
  mutate(count = n(), .after = "INIKA_OH_TZ_ID") %>%
  select(Gender, count) %>% 
  distinct()

This gives use the number of females and males.

This technique of grouping can become powerful, when you want to create contingency tables where you count interactions between groups.

human_data_selection_dedup %>% 
  select(Sample, Season, District) %>%
  arrange(Sample, Season, District) %>%
  group_by(Sample, Season, District) %>%
  mutate(count = n()) %>%
  distinct()

Here is normal that we have one with NA NA NA, because we did not finish the data cleaning.

2.4.9.2 Finding which unique ID are duplicated

… and which some rows are different beween duplicates

we can get a list of unique identifier in those that are duplicated, then we can check one my one

unique(duplicated_data$INIKA_OH_TZ_ID)

we filter out the incomplete data - to do this, we need to identify which column a way to identify it (example ID and what is missing in the most incomplete and not on the other)

duplicated_data %>% 
  filter(INIKA_OH_TZ_ID == "211") %>% 
print(width = Inf)

Note here I use “211” because ID is a type character. If it was a number I would have used 211 without the quotes. Errors can happen if you use a number instead of character or vice/versa.

Who_is_your_caretaker is NA in the row we want to suppress (less complete). There is no incompatibility between rows for this sample ID.

We first check that our code is working on the duplicated_data then when we are sure its ok, we can do that on the human_data_selection_dedup

  • We select the row that is not complete
duplicated_data %>% 
  filter(INIKA_OH_TZ_ID == "211" & is.na(Who_is_your_caretaker)) %>% 
  print(width = Inf) # you can change for view if its more convenient
  • To filter out, we need to use the OPPOSITE of the condition we used to select the row. This is where we use ! in front of the WHOLE condition of selection; the position of brackets are important.
human_data_selection_dedup <- 
  human_data_selection_dedup %>%
  filter(! (INIKA_OH_TZ_ID == "211" & is.na(Who_is_your_caretaker)) )

# Control that its the correct value is there and that there are no duplicates
human_data_selection_dedup %>% filter(INIKA_OH_TZ_ID == 211)

Note : if you do errors, you will have to rerun everything up to this point !

Now we can repeat those steps for the other duplicated data

duplicated_data %>% 
  filter(INIKA_OH_TZ_ID == "2374") %>% 
  print(width = Inf)

Here, we see we have a problem. The two raws of data are incompatible. If we can find out what data is correct, then we need to rectify the data. This is done by script - so we know it has been done (changes in the raw data are not desirable).

Assuming that The record with the Female is correct we can remove the row with the Male.

  • First I make sure I select the correct row
# Selecting the column that we want to remove
duplicated_data %>% 
  filter(INIKA_OH_TZ_ID == "2374" & Gender == "Male") %>% 
  print(width = Inf)
  • Then I remove the row from the data by choosing the OPPOSITE
# Removing the column - we filter by the negation of the column we want to remove
human_data_selection_dedup <- 
  human_data_selection_dedup %>%
  filter(! (INIKA_OH_TZ_ID == "2374" & Gender == "Male")) 

# Verification that the other data has been removed 
human_data_selection_dedup %>%
  filter(INIKA_OH_TZ_ID == "2374") 

you can see that verifying your data with code, allows you to track what you have done. It allows you also to document your choices, of what this was removed, and if you did a mistake you can fix it and rerun the code. The raw data is not distroyed, so you can rerun the code until you are satisfied you have found out all the data where quality was not good.

2.4.9.3 Exercise (we do not do during the course)

Then we need to continue the list of IDs where we detected data that was potentially incorrect (here we still have one: ID 23143)

duplicated_data %>% 
  filter(INIKA_OH_TZ_ID == "23143") %>% 
  print(width = Inf)

Remove the raw that is false (if you do not know, assume which one is false for the sake of the exercise)

  • select the raw with the wrong data
duplicated_data %>% 
  filter(INIKA_OH_TZ_ID == "23143" & Have_you_ever_heard_about_AMR == "No") %>% 
  print(width = Inf)
  • Select the opposite to and remove the raw with the wrong data from the data set
human_data_selection_dedup <- 
  human_data_selection_dedup %>%
  filter(! (INIKA_OH_TZ_ID == "23143" & Have_you_ever_heard_about_AMR == "No"))
  • Verification we still have the correct data
human_data_selection_dedup %>%
  filter(INIKA_OH_TZ_ID == "23143")

2.4.10 A Closer inspection of missing values - Finding out to with Identifier it corresponds.

  • here we only select eg. some columns that we know we can verify
colnames(human_data_selection_dedup)

subset_NA <- 
  human_data_selection_dedup %>%
  select(c(INIKA_OH_TZ_ID,Which_class_grade_are_you, Who_is_your_caretaker)) %>% # we made a subset of the dataframe 
  filter(if_any(everything(), is.na))  # everything means all the columns in the data frame

subset_NA

We have created a subset of data where columns have missing values.

We can save this subset into a spreasheet so you can. eg. print this data frame if you need to go back to some notes (eg. lab notes on paper) to verify those values.

write_csv(subset_NA, here::here("results", "subset_NA.csv"))

Should you replace few missing values, eg. if you had forgotten to register and if it was on a paper note, you can do that by selecting the cell where the value should be and then assigning it.

In our example we use the subset_NA to show that, but if you had to change the values they should go into the human_data_selection, which is the cleaned data that will be used later on for analysis.

  1. Finding the cell we want to change eg: ID 231 and the grade. Here we do an example on the subset, we do not change the data.

We show you how it can be done IF you can recover the missing data from your notes.

  • you can use mutate and if_else : condition, value condition true, value condition false
subset_NA %>% 
  mutate(Which_class_grade_are_you = 
           if_else(INIKA_OH_TZ_ID == "231", "Grade 10", Which_class_grade_are_you)) %>% 
  print(width = Inf)
  • A variant of if_else when you must replace several values
subset_NA %>% 
  mutate(Which_class_grade_are_you = 
           case_when(
             INIKA_OH_TZ_ID == "231" ~ "Grade 10",
             INIKA_OH_TZ_ID == "233" ~ "Grade 10",
             TRUE ~ Which_class_grade_are_you
           )) %>% 
  print(width = Inf)
  • when true it does that for the line.
  • the true at the end, is everything that was not true before.

Be careful if you use that, the order might matter if you use it in a complicated way!

2.4.11 Other summary statistics …

Example you want to know how many children of different ages you have in your data set

glimpse(human_data_selection_dedup)

Here is the fast to count how many children of different ages you have in your data set

human_data_selection_dedup %>% 
  group_by(Age__yrs) %>%
  summarise(n = n())

This is the long way to do the same thing … we had done like that before.

human_data_selection_dedup %>% 
  group_by(Age__yrs) %>%
  mutate(count = n()) %>%
  select(Age := Age__yrs, count) %>% # := allows to change the name of the variable while using it (practical !) 
  arrange(Age) %>%
  distinct() 

You can also optain summary statistics eg. the mean age of the children, the quantiles of the age distribution in the data, as well as the min and max.

human_data_selection_dedup %>% 
  summarise(mean_age = mean(Age__yrs, na.rm = TRUE),
            median_age = median(Age__yrs, na.rm = TRUE),
            quantile_25 = quantile(Age__yrs, probs = 0.25, na.rm = TRUE),
            quantile_75 = quantile(Age__yrs, probs = 0.75, na.rm = TRUE),
            min_age = min(Age__yrs, na.rm = TRUE),
            max_age = max(Age__yrs, na.rm = TRUE))

Remember in the beginning we used the summary function ? this can also be obtained like that. There are advantages and inconvenient in both methods. With sum arise, I can define exactly what I want to see, and how it should be calculated. Moreover this produce a data frame that I can export

human_data_selection_dedup %>% 
  select(Age__yrs) %>%
  summary()

Summarize is very powerful to calculate simple contingency tables for groups.

human_data_selection_dedup %>% 
  group_by(Gender) %>%
  summarise(mean_age = mean(Age__yrs, na.rm = TRUE),
            median_age = median(Age__yrs, na.rm = TRUE),
            quantile_25 = quantile(Age__yrs, probs = 0.25, na.rm = TRUE),
            quantile_75 = quantile(Age__yrs, probs = 0.75, na.rm = TRUE),
            min_age = min(Age__yrs, na.rm = TRUE),
            max_age = max(Age__yrs, na.rm = TRUE),
            .by_group = TRUE)

Another example :

human_data_selection_dedup %>% 
  group_by(Have_you_ever_heard_about_AMR, If_yes__how_did_you_get_this_information) %>%
  summarise(n = n())

This is a way to control if the data is consistent. If people would have both answered that they have not heard about AMR but if they had given a precision on where, then it might indicate error in data recording, or that people maybe did not answer properly to the questionnaire.

Some questionnaires have redundant and control questions, only formulated in a different ways, to check for the consistency of the data.

Ensuring the data quality, means that you have to think of all the things that could have gone wrong, and to check for them. The more you know your data the easier it gets to think about what could have gone wrong.

3 ! Madelaine check (to be sure its stil there)

%### Exercice : I discovered more data to recheck

Hum, it seems to me that we might still have one line with many missing values in the dataset Find a way to select the line with a lot of NAs (you can do many steps, as long at you manage at the end)

human_data_selection_dedup %>%
  filter(is.na(Gender) & is.na(Age__yrs))

3.0.1 Basic plotting to check your data

We can also explore our data visuellement.

Some basic plots can be used eg. to check consistencies between answers, or can serve as contingency tables

glimpse(human_data_selection_dedup)
human_data_selection_dedup %>% 
           ggplot(aes(x = Age__yrs, fill = Gender)) +
           geom_bar(stat = "count", position = "stack") +
           theme_minimal()  

In the plot above, we used factors. Sometimes using factors can be tricky, if some statistics are done on the values. So be careful.

I prefer to transform factors into characters, to avoid any confusion.

df_plot2 <- 
  human_data_selection_dedup %>% 
  mutate_if(is.factor , as.character) %>%
  filter(!is.na(Gender))

We can remove the NAs for nwo

  • small differences of options can make your plot look totally differently
ggplot(df_plot2,aes(x = Age__yrs, fill = Gender)) +
           geom_bar(stat = "count", position = "stack") +
           theme_minimal() 

See NAs are gone. You need to think if they are worth adding or not. What do they mean in your data ?

Small changes can make your plot look very different.

ggplot(df_plot2,aes(x = Age__yrs, fill = Gender)) +
           geom_bar(stat = "count", position = "stack") +
           theme_minimal() +
  facet_wrap(~Gender) +
  labs(title = "Number of interogated participant by age and Gender", 
       x = "Age (years)",
       y = "Number of participants") 

Here we show anotherway you can remove NAs in the factors

other_nonNA <- 
  human_data_selection_dedup %>% 
  filter(!is.na(Gender)) %>%
  mutate_if(is.factor, ~droplevels(.))

plot1 <- ggplot(other_nonNA,
       aes(x = Age__yrs, fill = Gender)) +
  geom_boxplot(na.rm = TRUE) +
  theme_minimal() +
  facet_wrap(~Gender) +
  labs(title = "Distribution of the age of participants by Gender", 
       x = "Age (years)",
       y = "Gender") 
plot1

We see that we have few values that are outlines. Here, its probably due¨ to the fact that most of the data was collected at schools.

We can check if this corresponds to our previous contingency table. However the outliers make it difficult to see the mean and median values on the graph

By removing the outliers in the display, it is easier to compare the central values of the distributioin,

plot_notouliers <- 
  ggplot(other_nonNA,
       aes(x = Age__yrs, fill = Gender)) +
  geom_boxplot(na.rm = TRUE, outliers = FALSE) +
  theme_minimal() +
  facet_wrap(~Gender) +
  labs(title = "Distribution of the age of participants by Gender", 
       x = "Age (years)",
       y = "Gender") 

plot_notouliers

Eg median age vale was : for females was 13 and 14 for males

  • You see that we changed the type of the plot by changing the geom
  • plots are done by adding different layers
  • as usual always have critical sense.
  • you can store plots as objects and view them by recalling the object
  • storing plot as objects allows exploring their content (this allows you to see if the data was used correctly)

Finding the data used in a plot

glimpse(plot_notouliers)

Each layer in the plot (basically all the things you can modify) can be inspected using $. Plots are fast to make, because there are a lot of defaults that are implicit. It is then fast, but sometimes it makes it difficult to know if you made your plot correctly.

plot_notouliers$data

colnames(plot_notouliers$data)

You can also compare plots or making com posit plots using the library aplot.

library(aplot)
plot1 / plot_notouliers 

3.1 Exporting our data (R format)

3.1.1 Rds format

  • We have seen previously that we can export a table to a csv file.

We can also export the content of a R Object to a file, this to be able to re-import it later on and eg. continue working on that, without having the hassle of checking that all the data types are read as we want.

saveRDS(human_data_selection_dedup, 
        here::here("results", "human_data_selection_dedup.rds"))

This is the command to allow to re-import the file later on to an object

my_table <- readRDS(here::here("results", "human_data_selection_dedup.rds")) 
tail(my_table) # shows the last rows of the table

3.1.2 Exercice : Exporting and importing data to and from a csv file

  • you can try to find back how to read, export the same data to a csv file.

  • hint : ?write.csv and ?read.csv

4 Some usefull tricks and notes

A computer does not like typos. The commands have to be typed correctly. Letter cases, spaces and symbols matter. Most errors of beginners are due to typos. It takes a certain time to be able to see them (weird but true). Some hidden characters (eg. end of lines, in windows car sometimes be problematic and cause errors)

  • Tab is your fried to avoid a lot of writing, it helps you auto-complete and go faster and without spelling errors

  • Escape when you had done typing error and that the console starts with a + (to abort your typing)

  • Ctrl + L to clear the console

  • Ctrl + Enter to run the current line

  • Ctrl + Alt + I to insert a new code chunk (might be different for you - write the shortcut here ! )

  • F1 to get the help on a function, of ?function.

  • Arrows up and down to recall or go down your history of commands in the console

  • Ctrl + S to save the file containing your code (! differ to git hub “commit as save your version” )

  • Do not forget about the possibility to visualize the data frame in another window, so you can look at it and write code at the same time.

  • When experimenting with code: commenting assignment and commenting/un-commenting pipes one step at the time allow you to check that what you are doing is as it should and pipes ca


A package that I find practical to use (but you can also search on the internet instead) when I do not know what functions a package contains is the package pacman. It allows you to look at which functions are contained in a package.

library(pacman)
p_functions(dplyr)

You can also use the embedded help function in Rstudio for that:

?dplyr # gives you info on where to find info
dplyr:: # and use the arrows to explore the different functions contained in the package

There are several exercises you can do here on your own. Try to understand what it does and use the help. I let those exercises because you should be able to modify them to use with other variables which can help you start (and avoid searching a lot on the internet).

I had actually to google to find out how to do things. And even if I have some experience with using R, I still control that the code is doing what it is supposed to do. Error is human !

5 When you are ready to go further:

You can use those lessons to repeat what you have learned (but in another way) and go further in your learning

The CRAN website you can find the reference manuals for R programming language. If you really want to understand the basics of the language you need to start reading the “introduction to R” manual.

5.1 Other ressources that I either used or look at and found if could be useful for you one day …

… and for me to remind where I found this information

Back to Index

LS0tDQp0aXRsZTogImByIHBhcmFtcyR0aXRsZWAiDQpkYXRlOiAiYHIgZm9ybWF0KFN5cy50aW1lKCksICclZCAlQiwgJVknKWAiDQphdXRob3I6IEV2ZSBaZXlsIEZpc2tlYmVjayBhbmQgTWFkZWxhaW5lIE5vcnN0csO2bQ0KcGFyYW1zOg0KICB0aXRsZTogIkNoYXAgMy4gQmFzaWMgUiBmb3IgZGF0YSBhbmFseXNpcyIgDQogIHByb2plY3RfcGF0aDogImByIGhlcmU6OmhlcmUoKWAiDQoNCmtuaXQ6IChmdW5jdGlvbihpbnB1dEZpbGUsIGVuY29kaW5nKSB7DQogIHJtYXJrZG93bjo6cmVuZGVyKGlucHV0RmlsZSwgZW5jb2RpbmcgPSBlbmNvZGluZywgb3V0cHV0X2RpciA9ICIuLi8uLi9kb2NzIikgfSkgIA0KICANCm91dHB1dDogDQogIA0KICANCiAgcm1kZm9ybWF0czo6cmVhZHRoZWRvd246DQogICAgICANCiAgICAgIGNzczogLi4vc3R5bGUuY3NzDQogICAgICBzZWxmX2NvbnRhaW5lZDogdHJ1ZQ0KICAgICAgY29kZV9kb3dubG9hZDogdHJ1ZQ0KICAgICAgdG9jX2RlcHRoOiA2DQogICAgICBkZl9wcmludDogcGFnZWQNCiAgICAgIGNvZGVfZm9sZGluZzogc2hvdw0KICAgICAgYXV0aG9yOiBwYXJhbXMkYXV0aG9yDQogICAgICBoaWdobGlnaHQ6IGVzcHJlc3NvDQogICAgICBudW1iZXJfc2VjdGlvbnM6IHRydWUNCmVkaXRvcl9vcHRpb25zOiANCiAgbWFya2Rvd246IA0KICAgIHdyYXA6IDcyDQogIGNodW5rX291dHB1dF90eXBlOiBjb25zb2xlDQotLS0NCg0KYGBge3Igc2V0dXAsIGluY2x1ZGU9RkFMU0V9DQprbml0cjo6b3B0c19jaHVuayRzZXQoZWNobyA9IFRSVUUsIGV2YWwgPSBUUlVFLCANCiAgICAgICAgICAgICAgICAgICAgICBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFLA0KICAgICAgICAgICAgICAgICAgICAgIHJlc3VsdHMgPSAnaGlkZScpDQpgYGANCg0KIyBBIGdvb2Qgb3JnYW5pc2F0aW9uIG9mIHlvdXIgcHJvamVjdCBmb3IgZGF0YSBhbmFseXNpcw0KDQpgYGB7ciAsIGVjaG8gPSBGQUxTRSwgcmVzdWx0cyA9ICdhc2lzJ30NCkRpYWdyYW1tZVI6Om1lcm1haWQoDQogICINCiAgZ3JhcGggVEINCiAgc3ViZ3JhcGggIA0KICAgIEFbUHJlcGFyaW5nIHlvdXIgd29ya10NCiAgICBBIC0tPiBDW0RpcmVjdG9yeSBzdHJ1Y3R1cmVdDQogICAgQyAtLT4gRFtQcm9qZWN0IFNldHVwXQ0KICAgIEQgLS0+IEQxW2RhdGFdDQogICAgRCAtLT4gRDJbcmVzdWx0c10NCiAgICBEIC0tPiBEM1tzcmMgT1IgY29kZV0NCiAgICBEIC0tPiBENFtiaW5dDQogICAgRCAtLT4gRDVbZG9jXQ0KICAgIEQgLS0+IEQ2WyAuLi4gXQ0KICBlbmQNCiAgc3ViZ3JhcGggIA0KICAgIEJbV29yayA6IG9uZSBvciBzZXZlcmFsIGFuYWx5c2VzXSAtLT4gRVtlZy4gQW5hbHlzaXMgb25lXQ0KICAgIEUgLS0+IEZbQ29kZV0NCiAgICBGIC0tPiBIW1Nlc3Npb24gU2V0dXBdDQogICAgRiAtLT4gSVtBbmFseXNpc10NCiAgICBGIC0tPiBLW1ByZXR0eWZ5aW5nXQ0KICAgIEggLS0+IEdbUmVzdWx0cyA6IG91dHB1dF0NCiAgICBJIC0tPiBHDQogICAgSyAtLT4gRw0KICBlbmQNCiAgIikNCmBgYA0KDQo8dT5GaWc6IFdvcmtmbG93IGFuZCBkaXJlY3RvcnkgcHJlcGFyYXRpb24gZm9yIGVhY2ggcHJvamVjdDwvdT4NCg0KUiBwcm9qZWN0XGAgRGlyZWN0b3J5ICYgc3RydWN0dXJlIDoNCg0KLSAgIEZhdm9ycyBnb29kIG9yZ2FuaXNhdGlvbg0KLSAgIEFsbG93cyBnaXQgLyB2ZXJzaW9uIGNvbnRyb2wNCi0gICBFYXNpZXIgdG8gc2hhcmUgLSBhbGxvd3Mgb3RoZXJzIHRvIGhhdmUgdGhlIHNhbWUgb3JnYW5pc2F0aW9uDQotICAgUmVwcm9kdWNpYmlsaXR5DQoNCkV4YW1wbGUgZnJvbSB0aGUgW0F1dGhvcmluZyBzY2llbnRpZmljIHB1YmxpY2F0aW9ucyB3aXRoIFINCk1hcmtkb3duXShodHRwczovL3Vjc2JjYXJwZW50cnkuZ2l0aHViLmlvL1ItbWFya2Rvd24vMDItaW50cm8vaW5kZXguaHRtbCk6DQoNCi0gICBbIF0g4oCCIGBkYXRhYCA6ICoqZm9yIHRoZSByYXcgZGF0YSBhbmQgYXNzb2NpYXRlZCBtZXRhZGF0YSAoY2FuIGJlDQogICAgbGlua2VkKSAhIE5vdCB1bmRlciB2ZXJzaW9uIGNvbnRyb2wqKg0KLSAgIFsgXSDigIIgYHJlc3VsdHNgIDogcHJvY2Vzc2VkIGRhdGEgYW5kIHJlc3VsdHMgb2YgYW5hbHlzZXMgLSB1bmRlcg0KICAgIHZlcnNpb24gY29udHJvbA0KLSAgIFsgXSDigIIgYHNyY2BPUiBgY29kZWA6IGZvciB0aGUgY29kZSB5b3Ugd3JpdGUgLSB1bmRlciB2ZXJzaW9uIGNvbnRyb2wNCg0KUGxlYXNlIG1ha2UgdGhvc2UgZGlyZWN0b3JpZXMgaWYgdGhleSBkbyBub3QgZXhpc3QgeWV0LCBpbiB5b3VyIHByb2plY3QNCg0KT3B0aW9uYWwgOg0KDQotICAgYGJpbmAgOiBmb3IgZXh0ZXJuYWwgY29kZSBvciBiaW5hcmllcyAoZWcuIGEgY29tcGlsZWQgcHJvZ3JhbSkgLQ0KICAgIChsaWtlbHkgbm90IHVuZGVyIHZlcnNpb24gY29udHJvbCkNCi0gICBgZG9jYCA6IHRleHQgZG9jdW1lbnRzIGFzc29jaWF0ZWQgdG8gdGhlIHByb2plY3QNCg0KIyMgRXhlcmNpc2U6IEdldHRpbmcgb3JnYW5pemVkIHRvIHByZXBhcmUgZGF0YSBhbmFseXNpcw0KDQotICAgWyBdIOKAgmNyZWF0ZSB0aGUgZGF0YSBhbmQgcmVzdWx0cyBkaXJlY3RvcmllcyBpbiB5b3VyIHByb2plY3QNCiAgICBkaXJlY3RvcnkNCi0gICBbIF0g4oCCY29weSB0aGUgZGF0YSBmcm9tIHRoZSAqKkh1bWFuIHByb2plY3QgKEtvYm9Ub29sYm94KSoqIGludG8gdGhlDQogICAgZGF0YSBkaXJlY3RvcnkNCg0KIyBGaXJzdCBzdGVwczogcHJlcGFyaW5nIG91ciBkYXRhIGJlZm9yZSBkYXRhIGFuYWx5c2lzDQoNCi0gICBXZSB3aWxsIHVzZSB0aGUgW3RpZHl2ZXJzZV0oaHR0cHM6Ly93d3cudGlkeXZlcnNlLm9yZy8pIG1ldGF2ZXJzZQ0KICAgIChzZXQgb2YgcGFja2FnZSB3aGF0IHdvcmsgd2VsbCB0b2dldGhlcikgdG8gbWFuaXB1bGF0ZSBkYXRhLg0KDQotICAgV2Ugd2lsbCBhbHNvIHRyeSB0byBzaG93IHlvdSB0aGUgcmVsYXRpb24gYmV0d2VlbiBiYXNlUiBhbmQgZHBseXIgKGENCiAgICBwYWNrYWdlIGZvciBkYXRhIG1hbmlwdWxhdGlvbiBpbiB0aWR5dmVyc2UpIHRvIGV4cGxhaW4gc29tZSBiYXNpYw0KICAgIHByb2dyYW1taW5nIGNvbmNlcHRzLg0KDQojIyBMb2FkaW5nIHJlcXVpcmVkIHBhY2thZ2VzDQoNCkJ5ICoqY29udmVudGlvbioqIHRoZXkgYXJlIGxvYWRlZCBhdCB0aGUgYmVnaW5uaW5nIG9mIGEgc2NyaXB0IG9yDQpkb2N1bWVudC4gKEhvd2V2ZXIsIGl0IHdvcmtzIHdlbGwgdG8gbG9hZCB0aGVtIHdoZW4gdGhleSBhcmUgbmVlZGVkKQ0KDQotICAgWyBdIOKAgmxvYWQgaGVyZSBhbmQgdGlkeXZlcnNlIHBhY2thZ2VzDQoNClVuaGlkZSB0byBzZWUgc29sdXRpb24NCg0KYGBge3IgbGlicmFyaWVzIGltcG9ydCwgY2xhc3Muc291cmNlID0gImZvbGQtaGlkZSJ9DQpsaWJyYXJ5KGhlcmUpDQpsaWJyYXJ5KHRpZHl2ZXJzZSkNCmBgYA0KDQotICAgWyBdIOKAgndoYXQgZG9lcyBtZWVuIHRoZSBncmVlbiB0aWNrcyBhbmQgcmVkIGNyb3NzZXMgPw0KLSAgIFsgXSDigIJ3aGljaCB0aWR5dmVyc2UgcGFja2FnZXMgYXJlIGxvYWRlZCA/DQotICAgWyBdIOKAgmNhbiB5b3Ugc2VlIHdoaWNoIHZlcnNpb24gd2FzIHVzZWQgZm9yIGVhY2ggcGFja2FnZSA/DQoNCiMjIFJlYWRpbmcgb3VyIGRhdGEgKG91ciBkYXRhIGlzIGNvbnRhaW5lZCBpbiBhIGRhdGFmcmFtZSkNCg0KDQpEYXRhIGZyYW1lcyAodGFidWxhciBmb3JtYXQgLSBzaW1pbGFyIHRvIHNwcmVhZHNoZWV0cykgLSBjb2x1bW5zIDoNCnZhcmlhYmxlcyAoaW5jbHVkaW5nIGlkZW50aWZpYW50KSAtIHJhd3MgOiBvYnNlcnZhdGlvbnMNCg0KDQpgYGB7ciB9DQpodW1hbl9kYXRhIDwtIA0KICByZWFkcjo6cmVhZF9jc3YyKA0KICAgIGhlcmU6OmhlcmUoImRhdGEiLCAiMjAyNC0wOS0yNV9JTklLQV9TQU1QTElOR19BTkFMWVNJU19IVU1BTi5jc3YiKQ0KICAgICkNCg0KYGBgDQoNCldlIG5lZWQgdG8gY2hlY2s6IA0KDQoxLiBJZiB0aGUgZGF0YSBoYXMgYmVlbiBsb2FkZWQgY29ycmVjdGx5IA0KMi4gSWYgdGhlIHR5cGVzIG9mIHRoZSBkYXRhIGluIGVhY2ggY29sdW1ucyBhcyByZWNvZ25pemVkIGNvcnJlY3RseQ0KDQoNClRvIGxvb2sgYXQgdGhlIHRhYmxlIGdyYXBoaWNhbGx5Og0KDQotICAgWyBdIOKAgiBFbnZpcm9ubWVudCBwYW5lIFw+IChjbGljaykgaHVtYW5fZGF0YSAoYWxzbyBjYW4gb3BlbiBpbiBhIG5ldyB3aW5kb3cpDQotICAgWyBdIOKAgiBBbHRlcm5hdGl2ZSB3aXRoIGNvZGUNCg0KYGBge3J9DQpWaWV3KGh1bWFuX2RhdGEpDQpgYGANCg0KWW91IGFyZSB1bmx1Y2t5IC0geW91IGhhdmUgdG8gbGVhcm4gYW5hbHl6aW5nIGEgaHVnZSB0YWJsZS4gVGhpcyBpcyBub3QNCmVhc2llc3QgdG8gc3RhcnQgd2l0aC4gTGV0cyBwcm9jZWVkIHN0ZXAgYnkgc3RlcC4gSW4gdGhlIGZvbGxvd2luZw0Kc2VjdGlvbnMsIHdlIHdpbGwgdHJhbnNmb3JtIHRoZSByYXcgZGF0YSB0byBhIGRhdGFzZXQgc28gaXQgYmVjb21lcyBiZQ0KZWFzaWVyIHRvIHdvcmsgd2l0aC4NCg0KPiBOb3RlOiBJIGFtIHdyaXR0aW5nIHRoZSBjb3Vyc2UgYXQgdGhlIHNhbWUgdGltZSBhcyBJIGRpc2NvdmVyIHRoZQ0KPiBkYXRhLiBTb21ldGhpbmdzIG1pZ2h0IGJlIGRvbmUgaW4gZGlmZmVyZW50IG9yZGVyLCBidXQgSSB3YW50IHlvdSB0bw0KPiBzZWUgaG93IEkgcHJvY2VzcyB0aGUgZGF0YSwgYW5kIHRoYXQgd2VsbCwgZXZlbiBpZiBJIGFtIG1vcmUNCj4gZXhwZXJpZW5jZWQgdGhhbiB5b3UsIEkgZG8gbm90IGtub3cgYWxsLiBTbyBJIHN0cnVnZ2xlIGFuZCBmaW5kDQo+IGluZm9ybWF0aW9uIHRvIEkgY2FuIGxlYXJuIGFuZCBnbyBmb3J3YXJkcy4NCg0KIyMjIEV4ZXJjaXNlDQoNCi0gICBbIF0g4oCCdXNlIHRoZSBoZWxwIHRvIGxvb2sgYXQgdGhlIG90aGVyIG9wdGlvbnMgdG8gcmVhZCB0aGUgdGFibGUuIEkNCiAgICB1c2VkIGRlZmF1bHQgZnVuY3Rpb25zIGZvciBzaW1wbGljaXR5LiBXaGF0IGFyZSB0aG9zZSA/DQotICAgWyBdIOKAgndoeSBkaWQgSSB1c2UgY3N2MiBpbnN0ZWFkIG9mIGNzdj8gdGhlIGV4dGVuc2lvbiBvZiBteSBmaWxlIHdhcw0KICAgIGNzdiByaWdodCEgRGlkIEkgZG8gYSBtaXN0YWtlPw0KDQotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCg0KIyMgRXhwbG9yaW5nIHRoZSBzdHJ1Y3R1cmUgb2YgdGhlIGRhdGENCg0KLSAgIFsgXSDigIIgd2hhdCBpcyB0aGUgc3RydWN0dXJlIG9mIHRoZSBkYXRhID8gKHRoZSBkYXRhIGlzIGluIGFuIG9iamVjdCwNCiAgICBzbyB3ZSBsb29rIGF0IHRoZSBzdHJ1Y3R1cmUgb2YgdGhlIG9iamVjdCkgSG93IG1hbnkgcm93cw0KICAgIChvYnNlcnZhdGlvbnMpIGFuZCBjb2x1bW5zICh2YXJpYWJsZXMpID8NCi0gICBbIF0g4oCCIFdlIGNhbiBsb29rIG1vcmUgaW4gZGV0YWlsIGF0IHRoZSBzdHJ1Y3R1cmUgb2YgdGhlDQogICAgcHJvZ3JhbW1hdGljYWxseQ0KDQpgYGB7ciAsIGNsYXNzLnNvdXJjZSA9ICJmb2xkLWhpZGUifQ0KIyB3ZSB1c2UgdGlkeXZlcnNlIHNvIGJldHRlciB0byBzdGF5IGluIHRoZSBzYW1lIHN5c3RlbQ0KZ2xpbXBzZShodW1hbl9kYXRhKQ0KDQojIGJ1dCB3ZSBjb3VsZCBoYXZlIHVzZWQNCiMgc3RyKGh1bWFuX2RhdGEpIA0KYGBgDQoNCiMjIyBUeXBlcyBvZiBkYXRhIGluIHRoZSBjb2x1bW5zDQoNCi0gICBbIF0g4oCCIFdlIGhhdmUgZGlmZmVyZW50IHR5cGVzOiBXZSBoYXZlIGRvdWJsZSAocmVhbCkgZGF0ZSwNCiAgICBjaGFyYWN0ZXIsIGxvZ2ljYWwuIE9vcHMgSSBoYWQgbmV2ZXIgc2VlbiBgZHR0bWAgdHlwZSBiZWZvcmUuDQoNCj4gSXQgc2VlbXMgdG8gYmUgc29tZXRoaW5nIGZvciBkYXRlcywgYnV0IEkgd2lsbCB1c2UgZ29vZ2xlIHRvIGZpbmQgb3V0DQp3aGF0IGl0IGlzIGV4YWNseTogYHdoYXQgaXMgZHR0bSBSIHR5cGVgLiBJIHNlZSBbUiBmb3IgZGF0YSBzY2llbmNlDQpwYWdlXShodHRwczovL3I0ZHMuaGFkLmNvLm56L2RhdGVzLWFuZC10aW1lcy5odG1sKSAtIGl0IHNlZW1zIHRvIGJlIGENCnJlbGlhYmxlIHNvdXJjZSBvZiBpbmZvcm1hdGlvbi4gSSBvcGVuIHRoZSB3ZWJwYWdlIHRoZW4gSSBzZWFyY2gNCmBkZHRtYCBpbiB0aGlzIHdlYiBwYWdlIGFuZCBJIHNlZSB0aGF0IGl0IGlzIGEgZGF0ZS10aW1lIHR5cGUgdXNlZCBieQ0KdGliYmxlcy4gSSBrbm93IHRpYmxlcyBhcmUgYSBuYW1lIHVzZWQgdG8gY2FsbCBhIHNwZWNpYWwgdHlwZSBvZg0KZGF0YWZyYW1lcyAodGliYmxlIHBhY2thZ2UgaW4gdGlkeXZlcnNlKS4gV2UgZG8gbm90IG5lZWQgdG8gYm90aGVyDQp3aXRoIGZ1cnRoZXIgaW5mb3JtYXRpb24gZm9yIG5vdy4NCg0KLSAgIFsgXSDigIIgd2hhdCBpcyB0aGUgdHlwZSBhbmQgY2xhc3Mgb2YgaHVtYW5fZGF0YSBvYmplY3Q/IA0KDQpgYGB7cn0NCnR5cGVvZihodW1hbl9kYXRhKQ0KY2xhc3MoaHVtYW5fZGF0YSkNCmBgYA0KDQo8IS0tIEEgZGF0YSBmcmFtZSBpcyBhIHNwZWNpYWwgdHlwZSBvZiBsaXN0LiANCkxpc3Qgd2l0aCB0aGUgc2FtZSBudW1iZXIgb2YgZWxlbWVudC4gDQpJbiBhIGNvbHVtbiwgdGhlIHR5cGVzIG9mIGVhY2ggZWxlbWVudCBhcmUgaWRlbnRpY2FsDQotLT4gDQoNClR5cGVzIG9mIHRoZSBkYXRhIGluIGNvbHVtbnMgYW5kIHR5cGVzIG9mIG9iamVjdHMgYXJlIGRpZmZlcmVudCB0aGluZ3MuIA0KDQpJIGRvIG5vdCBjaGFuZ2UgdGhlIHR5cGVzIHRoYXQgbmVlZCB0byBiZSBjaGFuZ2VkLCByaWdodCBub3cuIFRoaXMNCndpbGwgYmUgZWFzaWVyIHRvIGRvIGxhdGVyIG9uLiBUaGUgaW1wb3J0YW50IHRoaW5nIGlzIHRoYXQgdGhlIGRhdGEgYXBwZWFycw0KdG8gaGF2ZSBiZWVuIHJlYWQgY29ycmVjdGx5LiBXZSB3aWxsIG1ha2Ugc29tZSBjaGVja3MgdG8gYmUgc3VyZS4NCg0KIyMgRGF0YSB3cmFuZ2xpbmcgYW5kIHR5ZGluZyANCg0KIyMjIFJlbmFtaW5nIGNvbHVtbnMNCg0KSSBzZWUgdGhhdCB0aGVyZSBhcmUgc29tZSBzcGFjZXMgaW4gdGhlIGNvbHVtbiBuYW1lcy4gDQpDb2x1bW4gbmFtZXMgd2l0aCBzcGFjZXMgYXJlIG1vcmUgZGlmZmljdWx0DQp0byB3b3JrIHdpdGggKGl0cyBub3QgaW1wb3NzaWJsZSwgYnV0IGl0cyBub3QgcHJhY3RpY2FsKS4gDQoNClRoZXJlIGFyZSBhbHNvIA0Kb3RoZXIgc3BlY2lhbCBjaGFyYWN0ZXJzIGA/IC9gIHRoYXQgd2lsbCBtYWtlIHdvcmsgdGhlIHdvcmsgd2l0aCB0aGUgZGF0YSBkaWZmaWN1bHQuIA0KSSB3aWxsIGFsc28gcmVtb3ZlIHRob3NlLg0KDQpJIHdpbGwgY2hhbmdlIHRoZSBjb2x1bW4gbmFtZXMgdG8gcmVtb3ZlIHRoZSBzcGFjZXMuIA0KSSB3aWxsIGRvIHRoYXQgc3RlcCBieSBzdGVwLCB3aGljaCB3aWxsIGFsbG93IHVzIHRvIHZlcmlmeSB3aGF0IHdlIGFyZSBkb2luZyANCmF0IGVhY2ggc3RlcC4gDQoNCjEuIEkgY3JlYXRlIGFuIG9iamVjdCB0aGF0IGNvbnRhaW5zIHRoZSBjb2x1bW4gbmFtZXMgaW4gYW4gb2JqZWN0DQoyLiBJIGNyZWF0ZSBhIG5ldyBvYmplY3Qgd2l0aCBuZXcgY29sdW1uIG5hbWVzIChzcGFjZXMgcmVwbGFjZWQgYnkgYF9gKSwgDQogICAgYW5kIHRoZW4gcmVwbGFjaW5nIHRoZSBvdGhlciBzcGVjaWFsIGNoYXJhY3RlcnMgYnkgYF8gYA0KMy4gSSByZXBsYWNlIHRoZSBjb2x1bW4gbmFtZXMgaW4gdGhlIHRhYmxlIGJ5IHRoZSBuZXcgY29sdW1uIG5hbWVzLiANCg0KQXQgZWFjaCBzdGVwIHdlIHdpbGwgY2hlY2sgd2hhdCB3ZSBoYXZlIGRvbmUuDQoNCmBgYHtyfQ0KIyBzdGVwIDENCm9yaWdpbmFsX2NvbHVtbnMgPC0gY29sbmFtZXMoaHVtYW5fZGF0YSkNCm9yaWdpbmFsX2NvbHVtbnMNCg0KIyBzdGVwIDINCiMgcmVwbGFjaW5nIHNwYWNlcyANCm5ld19jb2x1bW5zIDwtIHN0cl9yZXBsYWNlX2FsbChvcmlnaW5hbF9jb2x1bW5zLCAiICIsICJfIikNCm5ld19jb2x1bW5zDQoNCiMgcmVwbGFjaW5nIHNwZWNpYWwgY2hhcmFjdGVycyANCm5ld19jb2x1bW5zIDwtIHN0cl9yZXBsYWNlX2FsbChuZXdfY29sdW1ucywgIls/Lyw7LiooKS1dIiwgIl8iKQ0KbmV3X2NvbHVtbnMNCg0KDQojIHJlbW92aW5nIF8gYXQgdGhlIGJlZ2lubmluZyBhbmQgZW5kIG9mIGNvbHVtbiBuYW1lcyAoc2hvdWxkIG5vdCBzdGFydCB3aXRoIF8pDQpuZXdfY29sdW1ucyA8LSBzdHJfcmVtb3ZlX2FsbChuZXdfY29sdW1ucywgIiheXyopfChfKiQpIikNCm5ld19jb2x1bW5zDQoNCiMgdGhpcyBjYW4gYWxzbyBiZSBkb25lLCBzdGlsbCBzdGVwIGJ5IHN0ZXAgdXNpbmcgcGlwZSAuLi4gbGlrZSB0aGF0DQojIHNwYWNlIGlzIHJlcGxhY2VkIGJ5IF8NCiMgYSBzZXJpZXMgb2YgLCBvciA7IG9yIC4gb3IgKiBvciAoIG9yICkNCg0KbmV3X2NvbHVtbnMgPC0gDQogIGNvbG5hbWVzKGh1bWFuX2RhdGEpICU+JQ0KICBzdHJfcmVwbGFjZV9hbGwoIiAiLCAiXyIpICU+JQ0KICBzdHJfcmVwbGFjZV9hbGwoICJbPy8sOy4qKCktXSIsICJfIikgJT4lDQogIHN0cl9yZW1vdmVfYWxsKCIoXl8qKXwoXyokKSIpDQoNCm5ld19jb2x1bW5zDQpgYGANCg0KVGhlcmUgaXMgZGV0YWlsZWQgaW5mb3JtYXRpb24gW2hlcmVdKGh0dHBzOi8vc3RyaW5nci50aWR5dmVyc2Uub3JnL2FydGljbGVzL3JlZ3VsYXItZXhwcmVzc2lvbnMuaHRtbCkNCm9uIGhvdyB0byB1c2UgcmVndWxhciBleHByZXNzaW9uIHdpdGggDQpgc3RyaW5ncmAgcGFja2FnZSBmdW5jdGlvbnMgDQoNCg0KTm93IHRoYXQgd2UgYXJlIHN1cmUgdGhlIG5ldyBjb2x1bW4gbmFtZXMgd2lsbCBiZSBlYXNpZXIgdG8gdXNlLCANCndlIGNhbiByZXBsYWNlIHRoZSBjb2x1bW4gbmFtZXMgaW4gdGhlIHRhYmxlLiANCldlIGNhbiBzdGlsbCByZWFkanVzdCB0aG9zZSBvbmVzIGxhdGVyIG9uIGlmIG5lZWRlZC4gDQoNCmBgYHtyfQ0KIyBTdGVwIDM6IFJlcGxhY2luZyB0aGUgY29sdW1uIG5hbWVzDQpjb2xuYW1lcyhodW1hbl9kYXRhKSA8LSBuZXdfY29sdW1ucw0KY29sbmFtZXMoaHVtYW5fZGF0YSkNCmBgYA0KDQoNCg0KIyMjIFNlbGVjdGluZyBjb2x1bW5zIHRvIHJlZHVjZSB0aGUgc2l6ZSBvZiB0aGUgdGFibGUgd2Ugd2lsbCB3b3JrIHdpdGggDQoNCk5vdyB3ZSBuZWVkIHRvIGZpbmQgY29sdW1ucyB3aXRoIHJlZHVuZGFudCBpbmZvcm1hdGlvbg0KdGhhdCB3ZSB3aWxsIG5vdCBuZWVkIGZvciB0aGUgYW5hbHlzaXMuIFRoaXMgd2lsbCBtYWtlIHRoZSBkYXRhIGZyYW1lIGVhc2llcg0KdG8gd29yayB3aXRoLg0KDQp3ZSBjYW4gZG8gdGhhdCBieSBjb2x1bW4gbmFtZXM6DQpgYGB7cn0NCmdsaW1wc2UoaHVtYW5fZGF0YSkNCg0KaHVtYW5fZGF0YSAlPiUgDQogIHNlbGVjdChJTklLQV9PSF9UWl9JRCwgQWdlX195cnMsIEdlbmRlcikgJT4lDQogIFZpZXcoLikNCmBgYA0KDQpgTkFgIHJlcHJlc2VudHMgbWlzc2luZyB2YWx1ZSAoaXRzIGtpbmQgb2YgYSBuZXV0cmFsIGZpbGxpbmcgb2YgdGhlIGNlbGwpDQoNCm9yIGJ5IGNvbHVtbiBudW1iZXJzIChzdGFydGluZyBmcm9tIDEpLCBpdCBjYW4gYmUgZWFzaWVyIHRoYW4gdG8gdHlwZQ0KYGBge3J9DQpjb2xuYW1lcyhodW1hbl9kYXRhKQ0KDQpodW1hbl9kYXRhICU+JSANCiAgc2VsZWN0KDMsNCw1KSAlPiUNCiAgaGVhZCguKQ0KDQpgYGANCg0KDQpUaGVyZSBhcmUgdG9vIG1hbnkgY29sdW1ucyBpbiB0aGUgZGF0YSwgc29tZSB3aXRoIHJlZHVuZGFudCBkYXRhIChlZy4NCnRoZSBkaWZmZXJlbnQgR2VuZGVyIGNvbHVtbnMsIGFuZCBtYW55IG90aGVycykuIA0KDQpIZXJlLCBGb3IgZGVtb25zdHJhdGlvbiBwdXJwb3NlcywgSSBzZWxlY3QgYSBzdWJzZXQgb2YgdGhlIGNvbHVtbnMgDQoodGhpcyB3aWxsIGJlIGVub3VnaCkNCg0KVHJpY2tzOiBUaGUgdGFiIGlzIGhlbHBmdWwgdG8gaGVscCB5b3Ugd3JpdGUgdGhlIGVuZCBvZiBjb2x1bW4gbmFtZXMNCihiYXNpY2FsbHkgdGhlIHF1ZXN0aW9ucyB0aGF0IGFyZSBkZXRhaWxlZCBhcyAwIG9yIDEgd2hlcmUgdGhlcmUgaXMgdGV4dCBjb2x1bW4NCmRvIG5vdCBuZWVkIHRvIGJlIHRha2VuIGluLikNCmBgYHtyfQ0KaHVtYW5fZGF0YV9zZWxlY3Rpb24gPC0gDQogIGh1bWFuX2RhdGEgJT4lDQogIHNlbGVjdChJTklLQV9PSF9UWl9JRCwgQWdlX195cnMsIEdlbmRlciwgRW50ZXJfYV9kYXRlLCBSZWdpb24sIERpc3RyaWN0LCANCiAgICAgICAgIFNwZWNpZnlfaWZfb3RoZXJfZGlzdHJpY3QsIFNhbXBsZSwgU2Vhc29uLCBPcmlnaW5fb2Zfc2FtcGxlLCANCiAgICAgICAgIFdoaWNoX2NsYXNzX2dyYWRlX2FyZV95b3UsIA0KICAgICAgICAgV2hvX2lzX3lvdXJfY2FyZXRha2VyLCANCiAgICAgICAgIElmX290aGVyc19fbWVudGlvbiwgDQogICAgICAgICBXaGF0X2lzX3lvdXJfb2NjdXBhdGlvbl9hbmRfb3Jfb2ZfeW91cl9jYXJldGFrZXIsIA0KICAgICAgICAgSGF2ZV95b3VfZXZlcl9oZWFyZF9hYm91dF9BTVIsIElmX3llc19faG93X2RpZF95b3VfZ2V0X3RoaXNfaW5mb3JtYXRpb24sIA0KICAgICAgICAgSGF2ZV95b3Vfb3JfeW91cl9jaGlsZHJlbl91c2VkX2FueV9hbnRpYmlvdGljc19hdF9hbnlfdGltZSwgDQogICAgICAgICBJZl95ZXNfX3doZXJlX2RpZF95b3VfZ2V0X3RoZXNlX2RydWdzX2Zyb20sDQogICAgICAgICBJZl9pdF93YXNfZHJ1Z19zZWxsZXJzX29yX3BoYXJtYWN5X19kaWRfeW91X2hhdmVfYV9wcmVzY3JpcHRpb25fZnJvbV90aGVfZG9jdG9yX3ByZXNjcmliZXIsDQogICAgICAgICBHUFNfY29vcmRpbmF0ZXNfbGF0aXR1ZGUsIEdQU19jb29yZGluYXRlc19sb25naXR1ZGUpIA0KDQpodW1hbl9kYXRhX3NlbGVjdGlvbiAlPiUgVmlldygpDQpgYGANCg0KDQoNCiMjIyBTb21lIGRhdGEgdmVyaWZpY2F0aW9uIGFuZCByZW1vdmluZyBvbmUgZW10cHkgY29sdW1uDQoNCg0KYGBge3J9DQpnbGltcHNlKGh1bWFuX2RhdGFfc2VsZWN0aW9uKQ0KYGBgDQoNCkkgc2VlIHRoYXQgYFNwZWNpZnlfaWZfb3RoZXJfZGlzdHJpY3RgIGlzIG9mIHR5cGUgbG9naWNhbCAoVFJVRSBvciBGQUxTRSkgYGxnbGANCnRoaXMgaXMgdW5leHBlY3RlZCwgYXMgaWYgc29tZXRoaW5nIGhhZCBiZWVuIHJlZ2lzdGVyZWQgaW4gaGVyZSwgdGhlcmUgc2hvdWxkIGJlIA0KdGV4dCAoY2hhcmFjdGVyIHR5cGUpDQoNCkkgc3VzcGVjdCB0aGF0IG5vdGhpbmcgaGFzIGJlZW4gcmVnaXN0ZXJlZC4gSSB3aWxsIGNoZWNrIHRoYXQuIA0KDQotIFRoaXMgaXMgYSB3YXkgdG8gbG9vayBhdCB0aGUgY29udGVudCBvZiB0aGUgY29sdW1uIG9ubHkNCg0KYGBge3J9DQpodW1hbl9kYXRhX3NlbGVjdGlvbiRTcGVjaWZ5X2lmX290aGVyX2Rpc3RyaWN0DQpgYGANCg0KLSBJIGNhbiBzZWUgaWYgdGhlcmUgaXMgc29tZXRoaW5nIHRoYXQgaXMgbm90IE5BIGJ5IG1ha2luZyBhIHNwZWNpZmljIHN1YnNldCANCm9mIHRoZSBjb2x1bW4sIGFuZCB0aGVuIGNvdW50aW5nIHRoZSBudW1iZXIgb2YgZWxlbWVudHMgaW4gdGhhdCBzdWJzZXQNCg0KYGBge3J9DQojIGNyZWF0ZSBhIGxvZ2ljYWwgdmVjdG9yIDogaWYgaXMgTk9UIE5BIHdpbCByZWdpc3RlciBUUlVFLCBvdGhlcndpc2UgaWYgTkEgd2lsbCBwdXQgRkFMU0UNCiMgQSBsb2dpY2FsIHZlY3RvciBhbGxvd3MgdG8gc2VsZWN0IHZhbHVlcyB0aGF0IGFyZSBUUlVFDQojIEV4YW1wbGUgDQoNCmxlYXJuX3Rlc3QgPC0gYyhOQSwgIm5vdCBlbXB0eSIsICIgIikNCiFpcy5uYShsZWFybl90ZXN0KQ0KdGVzdCA8LSBsZWFybl90ZXN0WyFpcy5uYShsZWFybl90ZXN0KV0NCnRlc3QNCmxlbmd0aCh0ZXN0KQ0KYGBgDQoNCldlIGRvIHRoZSBzYW1lIGZvciB0aGUgY29sdW1uIG9mIG91ciBkYXRhc2V0LiANCmBgYHtyfQ0KIWlzLm5hKGh1bWFuX2RhdGFfc2VsZWN0aW9uJFNwZWNpZnlfaWZfb3RoZXJfZGlzdHJpY3QpDQoNCnRlc3QgPC0gDQogIGh1bWFuX2RhdGFfc2VsZWN0aW9uJFNwZWNpZnlfaWZfb3RoZXJfZGlzdHJpY3RbIWlzLm5hKGh1bWFuX2RhdGFfc2VsZWN0aW9uJFNwZWNpZnlfaWZfb3RoZXJfZGlzdHJpY3QpXQ0KDQpsZW5ndGgodGVzdCkNCmBgYA0KDQpPaywgdGhpcyBjb2x1bW4gaXMgdG90YWxseSBlbXB0eSwgd2UgY2FuIHJlbW92ZSBpdCBmcm9tIHRoZSBkYXRhIHNldC4gDQpBIG5lZ2F0aXZlIHNlbGVjdGlvbiBhbGxvd3MgdG8gcmVtb3ZlIGNvbHVtbnMuDQpXZSBuZWVkIHRvIHJlcGxhY2UgdGhlIGRhdGEgZnJhbWUgd2l0aCB0aGUgbmV3IG9uZSAocmVhc3NpZ25tZW50KQ0KDQpgYGB7cn0NCmh1bWFuX2RhdGFfc2VsZWN0aW9uIDwtIA0KICBodW1hbl9kYXRhX3NlbGVjdGlvbiAlPiUNCiAgc2VsZWN0KC1TcGVjaWZ5X2lmX290aGVyX2Rpc3RyaWN0KQ0KDQojIHZlcmlmaWNhdGlvbiBpdCBoYXMgYmVlbiByZW1vdmVkDQpjb2xuYW1lcyhodW1hbl9kYXRhX3NlbGVjdGlvbikNCmBgYA0KDQojIyMgQ2hhbmdpbmcgdGhlIGRhdGEgdHlwZXMgb2Ygc29tZSBjb2x1bW5zDQoNCmBgYHtyfQ0KZ2xpbXBzZShodW1hbl9kYXRhX3NlbGVjdGlvbikNCmBgYA0KDQpJTklLQSBpZGVudGlmaWVyIG5lZWRzIHRvIGJlIHJlYWQgYXMgY2hhcmFjdGVyICh3ZSBkbyBub3Qgd2FudCB0byB0cmVhdCBpdCBhcyANCmEgbnVtYmVyIGJ1dCBhcyBhIHRleHQpDQoNClRoZSBhZ2UgKGluIHllYXJzKSBjb3VsZCBiZSBhbiBpbnRlZ2VyICh3aG9sZSBudW1iZXIpIGluc3RlYWQgb2YgYSBkb3VibGUuDQpXZSB3aWxsIGNoYW5nZSB0aGF0IA0KDQpZZXMvTm8gYW5zd2VycyBjYW4gYmUgdHJlYXRlZCBhcyBlaXRoZXIgbG9naWNhbCAoVFJVRS9GQUxTRSkgb3IgYXMgZmFjdG9ycw0KV2UgY2FuIGxvb2sgYXQgdGhlIGRpZmZlcmVudCBsZXZlbHMgKHRoZSBkaWZmZXJlbnQgdmFsdWVzLCBoZXJlIGFuc3dlcnMsIHRha2VuDQpieSBhIHZhcmlhYmxlKQ0KDQpgTkFgIChtaXNzaW5nIHZhbHVlcyBhcmUgTk9UIHRyYW5zZm9ybWVkIGludG8gYSBsZXZlbCBieSBkZWZhdWx0LCBieSBkZWZhdWx0DQp0aGV5IGFyZSBleGNsdWRlZCkuIFR5cGUgRjEgZm9yIGhlbHAuIA0KDQo+IEhlcmUgd2Ugd2lsbCB0cmVhdCB0aGVtIGFzIGEgbGV2ZWwgb2YgYSBmYWN0b3IsIGp1c3QgeW91IHNob3cgeW91IHRoYXQgeW91IG5lZWQNCnRvIGhhdmUgY3JpdGljYWwgc2Vuc2UsIG90aGVyd2lzZSB5b3UgbWlnaHQgbm90IGRldGVjdCBtaXN0YWtlcy4gDQpXZSB3aWxsIHNob3cgeW91IGhvdyB0byBjb3JyZWN0IHRoaXMgbWlzdGFrZXMuIA0KDQoqKk5vcm1hbGx5IHdlIGRvIG5vdCB1c2UgdGhlIGV4Y2x1ZGUgb3B0aW9uLCBidXQgSSB3YW50IHRvIHNob3cgeW91IGhvdyBpdCBhZmZlY3RzIHRoZSByZXN0LiBXZSB3aWxsIGZpeCB0aGF0IGxhdGVyKioNCiANCg0KYGBge3J9DQojIHRoaXMgaXMgYSB3YXkgDQpodW1hbl9kYXRhX3NlbGVjdGlvbiRIYXZlX3lvdV9ldmVyX2hlYXJkX2Fib3V0X0FNUg0KDQpsZXZlbHMoZmFjdG9yKGh1bWFuX2RhdGFfc2VsZWN0aW9uJEhhdmVfeW91X2V2ZXJfaGVhcmRfYWJvdXRfQU1SLCANCiAgICAgICAgICAgICAgICAgIGV4Y2x1ZGUgPSBOVUxMKSkNCmBgYA0KDQpBbm90aGVyIHdheSBpcyB0byBsb29rIGF0IHRoZSBkaXN0aW5jdCB2YWx1ZXMgb2YgdGhlIGNvbHVtbg0KYGBge3J9DQojIG9yIHRoaXMgaXMgYW5vdGhlciB3YXkgdXNpbmcgdGlkeXZlcnNlDQpodW1hbl9kYXRhX3NlbGVjdGlvbiAlPiUNCiAgc2VsZWN0KEhhdmVfeW91X2V2ZXJfaGVhcmRfYWJvdXRfQU1SKSAlPiUNCiAgZGlzdGluY3QoKQ0KYGBgDQoNCkkgd2FudCB0byB0cmVhdCB0aG9zZSBhcyBmYWN0b3IuIEkgd2lsbCBoYXZlIHRvIGNyZWF0ZSBhIHRyYW5zZm9ybWF0aW9uDQpvZiB0aGUgdGFibGVzIHR5cGVzIHRoYXQgYWNjb3VudCBmb3IgTkEgdmFsdWVzLiBJIHdhbnQgdG8ga25vdyBob3cgbWFueSANCmFuZCB3aGVyZSBkYXRhIGFyZSBtaXNzaW5nLiANCg0KRmlyc3QgSSB3aWxsIHNob3cgeW91IGhvdyB5b3UgY2FuIGNoYW5nZSB0eXBlcywgYW5kIGNoZWNrIHRoZSByZXN1bHQgYXQgdGhlIHNhbWUgdGltZS4NCkFmdGVyIHdlIHB1dCBldmVyeXRoaW5nIHRvZ2V0aGVyIGFuZCBkbyBpdCBpbiBvbmUgZ28sIGFuZCBtb2RpZnkgdGhlIHRhYmxlLg0KDQptdXRhdGU6IHRyYW5zZm9ybSB0aGUgZGF0YSAoaXQgY2FuIGJlIHR5cGVzLCBvciBzb21lIGNhbGN1bGF0aW9uLi4uKQ0KDQpgYGB7cn0NCiMgQSB3YXkgdG8gZG8gdGhpcw0KaHVtYW5fZGF0YV9zZWxlY3Rpb24gJT4lDQogIG11dGF0ZShJTklLQV9PSF9UWl9JRCA9IGFzLmNoYXJhY3RlcihJTklLQV9PSF9UWl9JRCkpICU+JQ0KICBnbGltcHNlKCkNCg0KIyBhbm90aGVyIHdheSB0byBkbyB0aGF0DQpodW1hbl9kYXRhX3NlbGVjdGlvbiAlPiUNCiAgbXV0YXRlX2F0KHZhcnMoSU5JS0FfT0hfVFpfSUQpLCBhcy5jaGFyYWN0ZXIpICU+JQ0KICBnbGltcHNlKCkNCmBgYA0KDQpXaGVuIHRoZXJlIGlzIG9ubHkgb25lIGNvbHVtbiwgaXRzIHBvc3NpYmxlIHRvIHdyaXRlIHRoZSBuYW1lIG9mIHRoZSBjb2x1bW4gbGlrZQ0KdGhhdCBhbmQgaXQgd2lsbCB3b3JrLiANCmBgYHtyfQ0KaHVtYW5fZGF0YV9zZWxlY3Rpb24gJT4lDQogIG11dGF0ZV9hdCgiSU5JS0FfT0hfVFpfSUQiLCBhcy5jaGFyYWN0ZXIpICAlPiUNCiAgZ2xpbXBzZSgpDQpgYGANCg0KQXMgeW91IGNhbiBzZWUsIHRoZXJlIGFyZSBzZXZlcmFsIHNvbHV0aW9ucywgcGljayBvbmUgdGhhdCB5b3UgdW5kZXJzdGFuZA0KDQo+IFlvdSBjYW4gYWxzbyBzZWUgdGhhdCBiZWNhdXNlIEkgZGlkIG5vdCByZWFzc2lnbiB0aGUgY2hhbmdlcywgdGhlbiBJIGNhbiANCnRlc3QgdGhlIHdheSB0byBkbyB0aGluZ3Mgd2l0aG91dCBtb2RpZnlpbmcgbXkgZGF0YS4gDQpXaGVuIEkgYW0gc3VyZSB0aGF0IHdoYXQgSSBkbyBpcyBjb3JyZWN0LCB0aGVuIEkgY2FuIHJlYXNzaWduIHRoZSBjaGFuZ2VzIHRvIHRoZQ0Kb3JpZ2luYWwgb2JqZWN0Lg0KDQo+IElmIEkgZG8gYSBtaXN0YWtlLCB3ZWxsLCB0aGlzIGlzIHN0aWxsIG5vdCBhIGNhdGFzdHJvcGhlLCBJIHJlcnVuIHRoZSB3aG9sZQ0KY29kZSB1bnRpbCB3ZXJlIEkgbWFkZSB0aGUgbWlzdGFrZSwgYW5kIGNhbiBjb3JyZWN0IHRoaXMgbWlzdGFrZS4NCg0KLS0tDQoNCk5vdyBJIHdhbnQgdG8gdHJhbnNmb3JtIG1hbnkgY29sdW1ucyB0byBmYWN0b3IuIEJlY2F1c2UgSSB3YW50IHRvIGNyZWF0ZSBhIA0Kc3VtbWFyeSBvZiB0aGUgdGFibGUsIHdoaWNoIHdpbGwgYmUgZWFzeSB0byBsb29rIGF0IGFuZCBjb3VudCB0aGUgbnVtYmVyIG9mDQpvYnNlcnZhdGlvbnMgaW4gZWFjaCBjYXRlZ29yeS4gDQoNCkkgY2FuIHNlbGVjdCBhbHNvIGJ5IGNvbHVtbiBudW1iZXIsIHRoaXMgYWxsb3dzIHRvIHNlbGVjdCBzbGljZXMgb2YgY29sdW1ucw0KDQpgYGB7cn0NCmNvbG5hbWVzKGh1bWFuX2RhdGFfc2VsZWN0aW9uKQ0KDQpodW1hbl9kYXRhX3NlbGVjdGlvbiAlPiUNCiAgbXV0YXRlX2F0KHZhcnMoMywgNToxOCksIGZhY3RvciwgIGV4Y2x1ZGUgPSBOVUxMKSAgJT4lDQogIGdsaW1wc2UoKQ0KICANCmBgYA0KDQoNCi0tLQ0KDQpBbmQgSSB3YW50IHRvIHRyYW5zZm9ybSB0aGUgYWdlIHRvIGludGVnZXIgKHRoYXQgd2lsbCBiZSB1c2VmdWwgZm9yIGRpc3BsYXkNCmluIGNhdGVnb3JpZXMpDQoNCmBgYHtyfQ0KaHVtYW5fZGF0YV9zZWxlY3Rpb24gJT4lDQogIG11dGF0ZSgiQWdlX195cnMiID0gYXMuaW50ZWdlcihBZ2VfX3lycykpICU+JQ0KICBnbGltcHNlKCkNCmBgYA0KDQotLS0NCg0KTm93IHdlIHB1dCB0b2dldGhlciBhbGwgdGhlIHN0ZXBzIGFuZCBtb2RpZnkgdGhlIHRhYmxlLiANCmBgYHtyfQ0KaHVtYW5fZGF0YV9zZWxlY3Rpb24gPC0gDQogIGh1bWFuX2RhdGFfc2VsZWN0aW9uICU+JQ0KICBtdXRhdGVfYXQodmFycyhJTklLQV9PSF9UWl9JRCksIGFzLmNoYXJhY3RlcikgJT4lDQogIG11dGF0ZSgiQWdlX195cnMiID0gYXMuaW50ZWdlcihBZ2VfX3lycykpICU+JQ0KICBtdXRhdGVfYXQodmFycygzLCA1OjE4KSwgZmFjdG9yLCAgZXhjbHVkZSA9IE5VTEwpIA0KDQpnbGltcHNlKGh1bWFuX2RhdGFfc2VsZWN0aW9uKQ0KYGBgDQoNCiMjIyBTdW1tYXJ5IG9mIHRoZSBkYXRhIDogYSBmYXN0IG92ZXJ2aWV3IG9mIHRoZSBkYXRhIGNvbnRhaW5lZCBpbiB0aGUgZGF0YXNldA0KDQpOb3cgd2UgY2FuIGhhdmUgYW4gb3ZlcnZpZXcgb2YgdGhlIGRhdGEgY29udGVudC4NCldlIHdpbGwgY291bnQgdGhlIG51bWJlciBvZiB2YWx1ZXMgdGFrZW4gYnkgZmFjdG9yIGFuZCBoYXZlIHNvbWUgc3VtbWFyeSBzdGF0aXN0aWNzDQpmb3IgdGhlIG51bWVyaWNhbCB2YWx1ZXMuIA0KDQpgYGB7cn0NCnN1bW1hcnkoaHVtYW5fZGF0YV9zZWxlY3Rpb24pDQpgYGANCg0KSXRzIHVzZWZ1bCBidXQgbm90IHZlcnkgbmljZS4gSXQgY2FuIGJlIGRvbmUgd2l0aCBkcGx5ciAuLi4gYnV0IGlzIG5vdCBhcyBjb21wbGV0ZS4NCkkgaGFkIHdyaXR0ZW4gYSBmdW5jdGlvbiB0aGF0IHdlIGNhbiB1c2UgdG8gaGF2ZSBhIGZvcm1hdHRlZCBzdW1tYXJ5LiANCldlIGNhbiB1c2UgZXh0ZXJuYWwgZnVuY3Rpb24gYnkgc291cmNpbmcgdGhlIGZpbGUuDQoNCiMjIyMgQm9udXMgLSBmb3JtYXR0aW5nIHRoZSBzdW1tYXJ5IHRhYmxlIGFuZCBleHBvcnQgaW4gYSBmaWxlICh3ZSBkbyBub3QgZG8gZHVyaW5nIHRoZSBjb3Vyc2UpDQoNClRoaXMgaXMgYSBsaXR0bGUgcXVpcmsgdG8gbWFrZSB0aGUgcmVzdWx0cyBtb3JlIHJlYWRhYmxlIHRvIG1ha2UgaXQgdmlzaWJsZSwgDQp3ZXJlIHdlIHRyYW5zZm9ybSB0aGUgc3VtbWFyeSBpbnRvIGEgZmFrZSB0YWJsZSAoZW10cHkgbGluZXMgYXJlIGFkZGVkKQ0KDQpUcnkgdG8gbG9hZCB0aG9zZSBwYWNrYWdlcywgaWYgaXQgZG9lcyBub3Qgd29yaywgeW91IG5lZWQgdG8gaW5zdGFsbCB0aGVtLCBiZWNhdXNlDQp0aGUgZnVuY3Rpb24gcmVsaWVzIG9uIHRob3NlIHBhY2thZ2VzDQoNCmBgYHtyfQ0KIyB0aGlzIGlzIHRoZSBwYXRoIG9mIHRoZSBmaWxlIA0KaGVyZTo6aGVyZSgic3JjIiwgImZvcm1hdF9zdW1tYXJ5X3N0YXRpc3RpY3NfZnVuLlIiKQ0KIyB0aGlzIGFsbG93cyB0byBsb2FkIHRoZSBmdW5jdGlvbiBjb250YWluZWQgaW50byB0aGUgZmlsZSBpbiB0aGUgbWVtb3J5DQpzb3VyY2UoaGVyZTo6aGVyZSgic3JjIiwgImZvcm1hdF9zdW1tYXJ5X3N0YXRpc3RpY3NfZnVuLlIiKSkNCmBgYA0KDQpXZSBzZWUgaW4gdGhlIGVudmlyb25tZW50IHBhbmVsIHRoYXQgdGhlIGBmb3JtYXRfc3VtbWFyeV9zdGF0aXN0aWNzYCBmdW5jdGlvbiBpcyBub3cgYXZhaWxhYmxlLg0KDQpXZSBjYW4gbG9vayBhdCB0aGUgY29kZSBvZiB0aGUgZnVuY3Rpb24gbGlrZSB0aGF0DQpgYGB7cn0NCmZvcm1hdF9zdW1tYXJ5X3N0YXRpc3RpY3NfZnVuDQpgYGANCg0KWW91IGNhbiBhbHNvIG9wZW4gdGhlIGZpbGUsIHRvIGxvb2sgYXQgaXQuIFRoaXMgaXMgYSBiaXQgdG8gYWR2YW5jZWQgZm9yIG5vdy4gDQpUaGVyZSBpcyBzb21lIGV4cGxhbmF0aW9uIGluY2x1ZGVkIGluIHRoZSBmaWxlIG9uIGhvdyB0byB1c2UgaXQuIA0KDQpgYGB7cn0NCmZvcm1hdF9zdW1tYXJ5X3N0YXRpc3RpY3NfZnVuKGh1bWFuX2RhdGFfc2VsZWN0aW9uKSAlPiUgVmlldygpDQpgYGANCg0KSXQgaXMgYSBiaXQgbW9yZSByZWFkYWJsZSwgaWYgeW91IGhhZCBhIGByZXN1bHRzYCBkaXJlY3RvcnkgdGhlbiBpdCBzaG91bGQgYmUNCnNhdmVkIHRoZXJlIGFzIGEgdHN2ICh0YWJ1bGF0aW9uIHNlcGFyYXRlZCB2YWx1ZXMpIGZpbGUuDQoNCiMjIyBGaWx0ZXJpbmcgb3V0IHVud2FudGVkIHZhbHVlcy4gDQoNCk9vcHMsIHdlIHNlZSB0aGF0IHdlIGhhdmUgcmVnaXN0ZXJlZCByb3dzIHRoYXQgc2hvdWxkIG5vdCBiZSB0aGVyZS4gDQpXZSB3aWxsIGZpbHRlciBvdXQgdGhvc2UgZGF0YSBmcm9tIHRoZSBkYXRhIHNldC4gDQoNCkFub3RoZXIgd2F5IHRvIGNvbmZpcm0gaXMgdG8gbG9vayBhdCB0aGUgcmFuZ2Ugb2YgdmFsdWVzDQpgYGB7cn0NCmh1bWFuX2RhdGFfc2VsZWN0aW9uICU+JQ0KICBzZWxlY3QoQWdlX195cnMpICU+JQ0KICByYW5nZShuYS5ybSA9IFRSVUUpDQpgYGANCg0KaG93IG1hbnkgb2YgdGhvc2UgZGF0YSB3ZSBuZWVkIHRvIHJlbW92ZSA/IA0KLSBieSB0cmFuc2Zvcm1pbmcgdGhlIGRhdGEgdHlwZSB0byBmYWN0b3IsIEkgY2FuIGhhdmUgYSBjb250aW5nZW5jeSB0YWJsZSBvZiB0aGUgDQpkaWZmZXJlbnQgdmFsdWVzIG9mIHRoZSB2YXJpYWJsZSAoaGVyZSAtIGFnZSkuIA0KYGBge3J9DQpmYWN0b3IoKGh1bWFuX2RhdGFfc2VsZWN0aW9uJEFnZV9feXJzKSwgZXhjbHVkZSA9IE5VTEwpICU+JQ0KICB0YWJsZSgpDQpgYGANCg0KV2Ugc2VlIHRoYXQgYHIgMTUgKyA3MmAgcm93cyBzaG91bGQgYmUgcmVtb3ZlZCBmcm9tIHRoZSBkYXRhIHNldC4gDQo8IS0tIGlubGluZSBjb21wdXRhdGlvbiBjYW4gYmUgdXNlZCwgd2hlbiB5b3UgcmVydW4gdGhlIGRhdGEsIHRoaXMgd2lsbCBiZSANCmF1dG9tYXRpY2FsbHkgdXBkYXRlZCBpbiB5b3VyIHRleHQgLS0+IA0KDQpXZSByZW1vdmUgdGhlIGRhdGEgdGhhdCB3ZSBkbyBub3Qgd2FudCBieSBhcHBseWluZyBhIGZpbHRlciwgdG8gb25seSBzZWxlY3QNCnJvd3MgZnJvbSB3aGljaCB2YWx1ZXMgaW4gYSBjZXJ0YWluIGNvbHVtbiBjb250YWlucyB2YWx1ZXMgd2Ugd2FudC4gDQpgYGB7cn0NCnRlc3QgPC0gDQogIGh1bWFuX2RhdGFfc2VsZWN0aW9uICU+JQ0KICBmaWx0ZXIoQWdlX195cnMgPj0gMTIpIA0KICANCm5yb3coaHVtYW5fZGF0YV9zZWxlY3Rpb24pDQpucm93KHRlc3QpDQpgYGANCg0KSW5saW5lX2NvZGU6IA0KV2UgZmlsdGVyZWQgb3V0IGByIG5yb3coaHVtYW5fZGF0YV9zZWxlY3Rpb24pIC0gbnJvdyh0ZXN0KWAgcm93cy4NCg0KDQpPb3BzIC4uLiBXZSBmaWx0ZXJlZCBvbmUgdG9vIG11Y2guIFdlIHJlbW92ZWQgdGhlIHJvdyB3aGVyZSB0aGUgdmFsdWUgd2FzIG1pc3NpbmcuDQpUaGUgY2hvaWNlIG9mIHJlbW92aW5nIHRoaXMgdmFsdWUgZGVmaW5pdGl2ZWx5IGRlcGVuZHMgb24gdGhlIGNvbnRleHQuIA0KQ2FuIHRoZSB2YWx1ZSBiZSByZWNvdmVyZWQgZnJvbSAoZWcuIGZpZWxkIG5vdGVzID8pIGFuZCB0aGVuIGNvcnJlY3RlZCBIRVJFICEuIA0KV2lsbCBpdCBpbmZsdWVuY2Ugb3VyIGFuYWx5c2VzIG9yIG5vdCA/IA0KQ2FuIGl0IGJlIGVzdGltYXRlZCBmcm9tIG90aGVyIHZhbHVlcyA/DQoNCjwhLS0gTWFkZWxhaW5lIG5lZWRzIHRvIGNvbW1lbnQgLS0+IA0KDQpGb3Igbm93IHdlIHdpbGwga2VlcCB0aGUgTkEgdmFsdWUgKCB3ZSBhcmUgbGVhcm5pbmcpDQpgYGB7cn0NCnRlc3QgPC0gDQogIGh1bWFuX2RhdGFfc2VsZWN0aW9uICU+JQ0KICBmaWx0ZXIoQWdlX195cnMgPj0gMTIgfCBpcy5uYShBZ2VfX3lycykpIA0KDQpucm93KGh1bWFuX2RhdGFfc2VsZWN0aW9uKSAtIG5yb3codGVzdCkgIA0KDQp0YWJsZShmYWN0b3IoKHRlc3QkQWdlX195cnMpLCBleGNsdWRlID0gTlVMTCkpDQpgYGANCg0KVGhpcyBub3cgY29ycmVjdC4gV2UgY2FuIGFwcGx5IHRoaXMgZmlsdGVyIHRvIHRoZSBkYXRhIGZyYW1lLiANCg0KYGBge3J9DQpodW1hbl9kYXRhX3NlbGVjdGlvbiA8LSANCiAgaHVtYW5fZGF0YV9zZWxlY3Rpb24gJT4lDQogIGZpbHRlcihBZ2VfX3lycyA+PSAxMiB8IGlzLm5hKEFnZV9feXJzKSkgDQpgYGANCg0KV2UgY2FuIGxvb2sgYWdhaW4gYXQgdGhlIGRhdGEgY29udGVudDogDQoNCmBgYHtyfQ0Kc3VtbWFyeShodW1hbl9kYXRhX3NlbGVjdGlvbikNCmBgYA0KDQoNCj4gTm90ZSA6IHlvdSBhbHdheXMgbmVlZCB0byBjaGVjayB0aGF0IHlvdSBhcmUgZG9pbmcgdGhlIHJpZ2h0IHRoaW5nLg0KWW91IGNhbiBhbHNvIHdyaXRlIHNtYWxsIGRhdGFzZXQgdGVzdHMgdG8gYmUgc3VyZSB0aGF0IHlvdSBkbyB0aGUgY29ycmVjdCB0aGluZy4NCldoZW4geW91IGhhdmUgd3JpdHRlbiBjb2RlLCBpdCBlYXN5IHRvIHJlcnVuIGl0IHdoZW4gd2UgaGF2ZSBmb3VuZCBvdXIgbWlzdGFrZXMgDQphbmQgY29ycmVjdGVkIHRoZW0uIEl0IGlzIG5vdCBwb3NzaWJsZSB0byBkZXRlY3QgbWlzdGFrZXMgYW5kIGNvcnJlY3QgdGhlbSB3aGVuDQp3ZSBjb3JyZWN0IHZhbHVlcyBvbiBhIHNwcmVhZHNoZWV0IGRpcmVjdGx5LiBUaGlzIGlzIHdoeSB0aGUgcmF3IGRhdGEgc2hvdWxkIE5PVA0KYmUgbW9kaWZpZWQgZGlyZWN0bHkuIA0KDQoNCiMjIyBTZWxlY3Rpbmcgcm93cyB3aXRoIG1pc3NpbmcgZGF0YSB0byBzZWUgaWYgd2UgY2FuIHJlY292ZXIgdGhlbSBmcm9tIGZpZWxkIG5vdGVzDQoNCmBgYHtyfQ0KaHVtYW5fZGF0YV9zZWxlY3Rpb24gJT4lDQogIGZpbHRlcihpcy5uYShBZ2VfX3lycykpICU+JQ0KICAjIHRoaXMgYWxsb3dzIHRvIGVuc3VyZSB0aGF0IHdlIHNlZSBhbGwgdGhlIGNvbHVtbnMgaW4gdGhlIGNvbnNvbGUgDQogIHByaW50KHdpZHRoPUluZikNCmBgYA0KDQpodW0sIGFsbCB0aGUgZGF0YSBvZiB0aGlzIElEIGFwcGVhcnMgdG8gYmUgbWlzc2luZy4NCldlIGNhbiByZW1vdmUgdGhpcyByYXcgZnJvbSB0aGUgZGF0YSBzZXQgDQoNCiMjIyMgRXhlcmNpc2UgOiByZW1vdmUgdGhlIHJvdyBmcm9tIHRoZSBkYXRhc2V0IA0KDQpSZW1vdmUgdGhlIHJvdyBjb250YWluaW5nIG1pc3NpbmcgdmFsdWUgaW4gQWdlIGZyb20gdGhlIGRhdGEgc2V0Lg0KDQpIaW50OiB0aGlzIG1lYW5zIHRoYXQgeW91IG5lZWQgdG8ga2VlcCBldmVyeXRoaW5nIHRoYXQgaXMgTm90IG1pc3NpbmcgKHB1dCAhIGluIA0KZnJvbnQgb2YgaXMubmEgY29tbWFuZCkuDQoNCkRvIG5vdCBmb3JnZXQgdG8gZG8gYSB0ZXN0LCBhbmQgY29udHJvbCB0aGF0IGlzIGRvZXMgd2hhdCB5b3Ugd2FudC4gDQoNCg0KYGBge3IgLCBjbGFzcy5zb3VyY2UgPSAiZm9sZC1oaWRlIn0NCiMgdGhpcyBzaG91bGQgd29yayAgdG8gb25seSBrZWVwIHRob3NlIHdpdGggTkENCnRlc3QgPC0gDQogIGh1bWFuX2RhdGFfc2VsZWN0aW9uICU+JQ0KICBmaWx0ZXIoIWlzLm5hKEFnZV9feXJzKSkNCg0KdGVzdA0KYGBgDQoNCg0KYGBge3IgLCBjbGFzcy5zb3VyY2UgPSAiZm9sZC1oaWRlIn0NCiMgY29udHJvbCAgLSBJIGNoZWNrIGlmIEkgZmluZCB0aG9zZSB3aXRoIE5BIGFnYWluIA0KdGVzdCAlPiUNCiAgZmlsdGVyKGlzLm5hKEFnZV9feXJzKSkgJT4lDQogICMgdGhpcyBhbGxvd3MgdG8gZW5zdXJlIHRoYXQgd2Ugc2VlIGFsbCB0aGUgY29sdW1ucyBpbiB0aGUgY29uc29sZSANCiAgcHJpbnQod2lkdGg9SW5mKQ0KDQojIE9SIGEgdHJpY2sgDQppcy5uYSh0ZXN0JEFnZV9feXJzKQ0Kc3VtKGlzLm5hKHRlc3QkQWdlX195cnMpKQ0KYGBgDQoNCg0KYGBge3IgLCBjbGFzcy5zb3VyY2UgPSAiZm9sZC1oaWRlIn0NCiMgVFJVRSBoYXMgYSB2YWx1ZSBvZiAxLCBGQUxTRSBoYXMgYSB2YWx1ZSBvZiAwDQojIGV4YW1wbGUgDQpzdW0oYyhUUlVFLCBGQUxTRSwgVFJVRSkpDQpgYGANCg0KTkI6ICBgYygpYCBmdW5jdGlvbiBpcyBhIHdheSB0byB3cml0ZSB2ZWN0b3JzIChvbmUgZGltZW5zaW9uYWwgYXJyYXlzKS4gDQoNCiMjIyBTZWxlY3Rpbmcgcm93cyB0aGF0IGNvbnRhaW5zIG1pc3NpbmcgZGF0YSBpbiBhbnkgY29sdW1uDQoNCklmIHdlIGhhdmUgZmV3IG1pc3NpbmcgZGF0YSwgd2UgY2FuIGdvIGZhc3RlciBpbiBvdXIgY2hlY2sgYnkgc2VsZWN0aW5nIHJvd3MNCnRoYXQgY29udGFpbnMgbWlzc2luZyBkYXRhIGluIGFueSBjb2x1bW4uIA0KDQoNCmBgYHtyfQ0KVmlldyhodW1hbl9kYXRhX3NlbGVjdGlvbikgDQojIEkgc2VlIHNvbWUgb3RoZXIgY29sdW1ucyB3aXRoIE5BIChzaG93IGhvdyB0byBkbykgLSBlZyB3aG8gaXMgeW91ciBjYXJldGFrZXINCg0KDQpodW1hbl9kYXRhX3NlbGVjdGlvbiAlPiUNCiAgZmlsdGVyKGlmX2FueShldmVyeXRoaW5nKCksIGlzLm5hKSkNCg0KYGBgDQoNCkV1aGggLi4uIHRoaXMgZG9lcyBub3Qgd29yayBhcyBpbnRlbmRlZCAuLi4gd2h5ID8gDQoNClRoaXMgaXMgYmVjYXVzZSBJIGhhdmUgdHJhbnNmb3JtZWQgdGhlIGRhdGEgdHlwZSBvZiB0aGUgY29sdW1ucyB0byBmYWN0b3IsIA0KYW5kIE5BIHZhbHVlcyBhcmUgY29uc2lkZXJlZCBhcyBhIGxldmVsLiANCg0KV2hlbiB3ZSBsb29rIGF0IGZhY3RvcnMsIHdlIHNlZSBjaGFyYWN0ZXJzIGxhYmVscy4gQWN0dWFsbHkgZmFjdG9ycyBhcmUgZW5jb2RlZA0KaW4gUiBhcyBpbnRlZ2Vycy4gTGV0cyB0cnkgdG8gdW5kZXJzdGFuZCB0aGlzLiANCg0KPiBIZXJlIGlzIGEgbGl0dGxlIHRlc3QgdG8gdW5kZXJzdGFuZCBob3cgZmFjdG9yIHdvcmtzLg0KDQpgYGB7ciBldmFsPUZBTFNFfQ0KIyBJIGNyZWF0ZSBhIHZlY3RvciBjaGFyYWN0ZXIgd2l0aCBzb21lIHZhbHVlcw0KdGVzdF92ZWN0b3IgPC0gYygiMSIsICIzIiwgIkMiLCBOQSkNCnRlc3RfZmFjdG9yIDwtIGZhY3Rvcih0ZXN0X3ZlY3RvciwgZXhjbHVkZSA9IE5VTEwpDQoNCnRlc3RfZmFjdG9yDQphcy5pbnRlZ2VyKHRlc3RfZmFjdG9yKSAjIGl0cyBhIGZhY3RvciAtIGl0IGNhbiBiZSBjb2VyY2VkIHRvIGEgbnVtYmVyDQphdHRyaWJ1dGVzKHRlc3RfZmFjdG9yKQ0KDQphcy5pbnRlZ2VyKHRlc3RfdmVjdG9yKSAjIGl0cyBjaGFyYWN0ZXIgLSAiQyIgbGV0dGVyIGNhbm5vdCBiZSBjb2VyY2VkICgiMSIgY2FuIGJlKQ0KYXR0cmlidXRlcyh0ZXN0X3ZlY3RvcikNCg0KIyBhbm90aGVyIGV4YW1wbGUgDQp0ZXN0IDwtIGZhY3RvcigyOjQpDQp0ZXN0DQoNCmFzLmludGVnZXIodGVzdCkgIyB0aGUgbGV2ZWxzIHN0YXJ0cyBhdCAxIC4uLiB0aGUgbGV2ZWxzIGFyZSBmaWxlZCBpbiBvcmRlciB0aGV5IGFyZSBwb3B1bGF0ZWQNCg0KIyB0ZXN0IA0Kd2hpY2gobGV2ZWxzKHRlc3RfZmFjdG9yKSA9PSAiQyIpDQp3aGljaChsZXZlbHModGVzdF9mYWN0b3IpID09IE5BKSAjIHRoaXMgaXMgbm90IHRoZSBjb3JyZWN0IHdheSB0byB0ZXN0LCBOQSBpcyBwYXJ0aWN1bGFyDQoNCmlzLm5hKGxldmVscyh0ZXN0X2ZhY3RvcikpDQp3aGljaChpcy5uYShsZXZlbHModGVzdF9mYWN0b3IpKSkNCmBgYA0KDQpPaywgc28gd2UgbmVlZCB0byBjaGFuZ2UgdGhlIGRhdGEgdHlwZSBvZiB0aGUgY29sdW1ucyBhZ2FpbiwgdG8gYmUgYWJsZSB0byANCnNlZSB3aGljaCBmYWN0b3JzIGNvbnRhaW5zIE5BLiANCg0KPiBOb3RlOiBZb3Ugc2VlIHdoeSBpdHMgaW1wb3J0YW50IHRvIGhhdmUgY3JpdGljYWwgc2Vuc2UuIEEgc2luZ2xlIGVycm9yIGF0IGENCmxpbmUgY2FuIGluZHVjZSBlcnJvciB0byB5b3VyIGRhdGFzZXQuIFRoZXJlZm9yZSB5b3UgbmVlZCB0byBjaGVjayB0aGF0IA0KdGhlIGNvZGUgeW91IHdyaXRlIGRvZXMgd2hhdCB5b3Ugd2FudC4NCg0KVG8gY2hhbmdlIHRoZSBkYXRhIHR5cGVzIG9mIGZhY3RvcnMsIHdlIG5lZWQgdG8gcmV2ZXJ0IGZpcnN0IHRvIGNoYXJhY3RlcnMsIHRoZW4NCndlIGNhbiByZS1jaGFuZ2UgdG8gZmFjdG9yIGFuZCB0aGlzIHRpbWUgTkEgd2lsbCBub3QgYmUgaW5jbHVkZWQgaW4gdGhlIGxldmVscy4gDQoNCmBgYHtyfQ0KIyBjaGFuZ2luZyB0aGUgZGF0YSB0eXBlcyAoYSByZW1pbmRlcikNCiMgbXV0YXRlIGlmIGRvZXMgYSB0ZXN0IC0gaWYgdGhlIHRlc3QgaXMgVHJ1ZSB0aGVuIHRoZSB0eXBlIGNoYW5nZSBpcyBhcHBsaWVkDQp0ZXN0IDwtIA0KICBodW1hbl9kYXRhX3NlbGVjdGlvbiAlPiUNCiAgbXV0YXRlX2lmKGlzLmZhY3RvciwgYXMuY2hhcmFjdGVyKSAlPiUNCiAgbXV0YXRlX2lmKGlzLmNoYXJhY3RlciwgYXMuZmFjdG9yKSANCg0KZ2xpbXBzZSh0ZXN0KQ0KDQpsZXZlbHModGVzdCRXaG9faXNfeW91cl9jYXJldGFrZXIpICAjIE5BIGFyZSBub3QgaW4gbGV2ZWxzIA0KdW5pcXVlKHRlc3QkV2hvX2lzX3lvdXJfY2FyZXRha2VyKSAgIyBidXQgdGhlIGFyZSBsaXN0ZWQgaW4gdW5pcXVlIGFzIG1pc3NpbmcgdmFsdWVzDQoNCiMgZ29vZCB0aGlzIHdvcmtlZCAtIHNvIHdlIGNhbiBtYWtlIHRoZSBjaGFuZ2VzIHRvIHRoZSBkYXRhIGZyYW1lDQpodW1hbl9kYXRhX3NlbGVjdGlvbiA8LSB0ZXN0DQpgYGANCg0KTm93IHdlIGNhbiB0ZXN0IGFnYWluIGlmIHdlIGhhdmUgbWlzc2luZyB2YWx1ZXMgaW4gYW55IGNvbHVtbi4gDQoNCmBgYHtyfQ0KaHVtYW5fZGF0YV9zZWxlY3Rpb24gJT4lDQogIGZpbHRlcihpZl9hbnkoZXZlcnl0aGluZygpLCBpcy5uYSkpIA0KYGBgDQoNCmBgYHtyfQ0KaHVtYW5fZGF0YV9zZWxlY3Rpb24gJT4lDQogIGZpbHRlcihpZl9hbnkoZXZlcnl0aGluZygpLCBpcy5uYSkpICU+JQ0KICBkaW0oKQ0KYGBgDQoNCldlIGhhdmUgbWlzc2luZyBtYW55IHZhbHVlcyBpbiB0aGUgZGF0YSBzZXQsIGluIDQwMiByb3dzIG92ZXIgMjAgY29sdW1ucy4NCkxldHMgaGF2ZSBhbm90aGVyIGxvb2sgYXQgdGhlIHN1bW1hcnksIGlzIHRoaXMgZGlzdHJpYnV0ZWQgb3ZlciBhbGwgY29sdW1ucw0Kb3IgYXJlIHRoZXJlIHNvbWUgY29sdW1ucyB3aGVyZSBpdHMgZXhwZWN0ZWQgdG8gaGF2ZSBtYW55IE5BcyA/IA0KYGBge3J9DQpzdW1tYXJ5KGh1bWFuX2RhdGFfc2VsZWN0aW9uICU+JSBzZWxlY3QoLUlOSUtBX09IX1RaX0lEKSkNCmBgYA0KDQo8IS0tIA0KWzFdICJXaGljaF9jbGFzc19ncmFkZV9hcmVfeW91IiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJXaG9faXNfeW91cl9jYXJldGFrZXIiICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgDQpbM10gIklmX290aGVyc19fbWVudGlvbiIgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIklmX3llc19faG93X2RpZF95b3VfZ2V0X3RoaXNfaW5mb3JtYXRpb24iICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICANCls1XSAiSWZfeWVzX193aGVyZV9kaWRfeW91X2dldF90aGVzZV9kcnVnc19mcm9tIiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiSWZfaXRfd2FzX2RydWdfc2VsbGVyc19vcl9waGFybWFjeV9fZGlkX3lvdV9oYXZlX2FfcHJlc2NyaXB0aW9uX2Zyb21fdGhlX2RvY3Rvcl9wcmVzY3JpYmVyIg0KLS0+IA0KDQoNCiJJZl9vdGhlcnNfX21lbnRpb24iLCAiSWZfeWVzX19ob3dfZGlkX3lvdV9nZXRfdGhpc19pbmZvcm1hdGlvbiIsIA0KIklmX3llc19fd2hlcmVfZGlkX3lvdV9nZXRfdGhlc2VfZHJ1Z3NfZnJvbSIsICANCiJJZl9pdF93YXNfZHJ1Z19zZWxsZXJzX29yX3BoYXJtYWN5X19kaWRfeW91X2hhdmVfYV9wcmVzY3JpcHRpb25fZnJvbV90aGVfZG9jdG9yX3ByZXNjcmliZXIiDQoNCkFyZSBjb2x1bW5zIHdlIGNhbiBleHBlY3QgbWFueSBOQXMgZHVlIHRvIHRoZSBxdWVzdGlvbiB3YXMgYXNrZWQsIA0KYmVjYXVzZSBpdCB3YXMgYSBjb250aW51YXRpb24gb2YgYSBwYXJ0aWN1bGFyIGNhc2UuIA0KDQpIb3dldmVyLCB3ZSBuZWVkIHRvIGluc3BlY3QgY2xvc2VyIHRoZSBjb2x1bW5zDQoiV2hpY2hfY2xhc3NfZ3JhZGVfYXJlX3lvdSIsIGFuZCAiV2hvX2lzX3lvdXJfY2FyZXRha2VyIiAgDQoNCg0KIyMjIyBFeGVyY2lzZSA6IENvdW50aW5nIHRoZSBudW1iZXIgb2YgTkFzIGluIGVhY2ggY29sdW1uICAod2UgZG8gbm90IGRvKQ0KLi4uIGFuZCB1c2luZyBpdCB0byBmaW5kIGNvbHVtbiB3aXRoIG1hbnkgTkFzDQoNCldlIGNhbiBoYXZlIGEgZGlmZmVyZW50IGFwcHJvYWNoIHRvIGZpbmQgd2hpY2ggY29sdW1ucyBjb250YWluaW5nIG1hbnkgTkFzLA0KZWcgYnkgY291bnRpbmcgdGhlIG51bWJlciBvZiBOQXMgaW4gZWFjaCBjb2x1bW4uIA0KDQpgYGB7ciAsIGNsYXNzLnNvdXJjZSA9ICJmb2xkLWhpZGUifQ0KIyBmaWx0ZXIgY29sdW1ucyB0aGF0IGNvbnRhaW4gbW9yZSB0aGFuIDEwIE5Bcw0KdGVtcCA8LSBodW1hbl9kYXRhX3NlbGVjdGlvbiAlPiUNCiAgbXV0YXRlKGFjcm9zcyhldmVyeXRoaW5nKCksIGlzLm5hKSkgJT4lICMgdGhpcyB3aWxsIHRyYW5zZm9ybSB0aGUgZGF0YSBmcmFtZSB0byBUUlVFL0ZBTFNFIChUUlVFIGl0IGl0cyBuYSkNCiAgc3VtbWFyaXNlX2FsbChzdW0pICMgYmVjYXVzZSBUUlVFIGlzIDEsIEZBTFNFIGlzIDAsIHdlIGNhbiBzdW0gdG8gY291bnQgdGhlIG51bWJlciBvZiBOQXMNCg0KdGVtcCAjIHdoZXJlIHdlIHNlZSBob3dtIG1hbnkgTmFzIHdlIGhhdmUNCg0KaHVtYW5fZGF0YV9zZWxlY3Rpb24gJT4lDQogIG11dGF0ZShhY3Jvc3MoZXZlcnl0aGluZygpLCBpcy5uYSkpICU+JQ0KICBzdW1tYXJpc2VfYWxsKHN1bSkgJT4lDQogIHNlbGVjdCh3aGVyZSh+c3VtKC4pID4gMTApKSAlPiUgIyBub3cgd2Ugc2VsZWN0IHRoZSBjb2x1bW5zIHdoZXJlIHRoZSBzdW0gaXMgYWJvdmUgMTANCiAgY29sbmFtZXMoKSAjIGFuZCB3ZSBnZXQgdGhlIGNvbHVtbiBuYW1lcyB3aGVyZSB0aGVyZSBhcmUgbW9yZXMgdGhhbiAxMCBOQXMNCg0KIyB0cmljayBjb21tZW50ICU+JSBhbmQgZXhlY3V0ZSBiZWZvcmUgdG8gc2VlIHdoYXQgaXQgZG9lcw0KYGBgDQoNCm5vdyB3ZSBoYXZlIG9idGFpbmVkIHRoZSBsaXN0IG9mIGNvbHVtbnMgd2l0aCBOQSB0aGF0IEkgaGFkIHB1dCBpbiB0aGUgY29tbWVudHMNCihoaWRkZW4gaW4gaHRtbCkgYWJvdmUuIA0KDQojIyMgQ2hlY2tpbmcgdGhhdCBJRHMgYXJlIHVuaXF1ZSAtIERpc3RpbmN0IHZhbHVlcyB1c2FnZQ0KDQpXZSBuZWVkIHRvIGVuc3VyZSB0aGF0IElEcyBhcmUgdW5pcXVlLiBJZiBub3QsIHRoZXJlIGlzIHByb2JhYmx5IA0KYSBwcm9ibGVtIHdpdGggdGhlIGRhdGEuIFRoZSBkYXRhIG1pZ2h0IGhhdmUgYmVlbiByZWdpc3RlcmVkIGluY29tcGxldGVseSBvciANCnNldmVyYWwgdGltZXMuIFdlIG5lZWQgYWxzbyB0byBjaGVjayB0aGF0IGlmIHdlIGhhdmUgZHVwbGljYXRlZCBJRHMgdGhhdA0KdGhlIGluZm9ybWF0aW9uLCBpZiBpbmNvbXBsZXRlbHkgcmVnaXN0ZXJlZCBpcyBub3QgaW5jb21wYXRpYmxlLiBUaGlzIGNhbiBhbGxvdw0KdG8gaWRlbnRpZnkgZXJyb3JzIGluIGRhdGEgcmVnaXN0cmF0aW9uLiANCg0KPiBOQjogU3VjaCBlcnJvcnMgYXJlIG5vdCB1bmNvbW1vbg0KDQpXZSBuZWVkIGZpcnN0IHRvIHJlbW92ZSBldmVudHVhbCBkdXBsaWNhdGVkIHJvd3MsIHdoZXJlIHRoZSBJRCBhbmQgYWxsIHZhbHVlcyBpbiANCmFsbCBjb2x1bW5zIGFyZSBpZGVudGljYWwuIA0KSWYgc29tZSBjb2x1bW5zIGFyZSBlbXB0eSwgdGhpcyBtZWFucyB0aGVyZSBjYW4gDQpoYXZlIGJlZW4gc29tZSBwcm9ibGVtcyBkdXJpbmcgdGhlIHJlZ2lzdGVyaW5nIG9mIHRoZSBkYXRhIChlZy4gc2VudCBzZXZlcmFsDQp0aW1lcyBhbmQgYWNjZXB0ZWQgYnkgdGhlIGRhdGFiYXNlKS4NCg0KLSBEaXN0aW5jdCBhbGxvd3MgdG8gZmlsdGVyIG91dCByb3dzIHRoYXQgYXJlIHRvdGFsbHkgaWRlbnRpY2FsIChpdCBtZWFucyBpZGVudGljYWwgDQppbiBhbGwgY29sdW1ucykNCmBgYHtyfQ0KZGltKGh1bWFuX2RhdGFfc2VsZWN0aW9uKQ0KDQpodW1hbl9kYXRhX3NlbGVjdGlvbl9kZWR1cCA8LSANCiAgaHVtYW5fZGF0YV9zZWxlY3Rpb24gJT4lDQogIGRpc3RpbmN0KCkgDQoNCmRpbShodW1hbl9kYXRhX3NlbGVjdGlvbl9kZWR1cCkNCmBgYA0KDQpgciBkaW0oaHVtYW5fZGF0YV9zZWxlY3Rpb24pWzFdIC0gZGltKGh1bWFuX2RhdGFfc2VsZWN0aW9uX2RlZHVwKVsxXWAgKipyb3dzIG9mIHRvdGFsbHkgaWRlbnRpY2FsIHJvd3Mgd2VyZSByZW1vdmVkKioNCg0KTm93IHdlIG5lZWQgdG8gY29uc2lkZXIgcm93cyB3aGVyZSB0aGUgSUQgaXMgZHVwbGljYXRlZCwgYnV0IHRoZSByZXN0IG9mIA0KdGhlIGRhdGEgaXMgbm90IGlkZW50aWNhbC4gDQpBZ2FpbiB0aGlzIGNhbiBiZSBhbiBlcnJvciBpbiBkYXRhIGhhdmluZyBiZWluZyBmaXJzdCBwYXJ0aWFsbHkgcmVnaXN0cmVkLCBmb2xsb3dlZA0KYnkgYSBiZXR0ZXIgcmVnaXN0ZXJpbmcsIG9yIGh1bWFuIGVycm9yIChlZy4gdXNpbmcgdGhlIHNhbWUgSUQpDQoNCg0KYGBge3J9DQpnbGltcHNlKGh1bWFuX2RhdGFfc2VsZWN0aW9uX2RlZHVwKQ0KYGBgDQoNCkhlcmUgdGhlcmUgaXMgc29tZXRoaW5nIEkgZGlkIG5vdCByZWNoZWNrLiANCkkgd2lsbCBuZWVkIHRvIHVzZSB0aGUgSUQgdG8gZmlsdGVyIHJvd3MgdGhhdCBhcmUgcHJvYmxlbWF0aWMgYW5kIGluc3BlY3QgdGhlbSBmdXJ0aGVyLg0KQnV0IEl0IGlzIG5vdCBwcmFjdGljYWwgZG9pbmcgdGhhdCB3aGVuIHRoZSBJRHMgYXJlIG9mIHR5cGUgZmFjdG9yICh0aGlzIGNvbXBsaWNhdGUgbWFraW5nDQp0aGUgY29ycmVzcG9uZGVuY2UgYmV0d2VlbiBhY3R1YWwgSUQgYW5kIGZhY3RvciB2YWx1ZSkuIFNvIEkgcHJlZmVyIHRvIHJlLWNvbnZlcnQgDQp0aGUgSUQgdG8gY2hhcmFjdGVyLiANCg0KPiBOb3RlOiBJIHNob3VsZCBoYXZlIHRob3VnaHQgYmV0dGVyIHdoZW4gSSBvcmlnaW5hbGx5IGNvbnZlcnRlZCB0aGUgdHlwZXMuIA0KVGhlIGJlc3Qgd2F5IHRvIGRvIHRoaW5ncyBmYXN0ZywgaXMgbm90IGFsd2F5cyBldmlkZW50IGF0IGZpcnN0LiBUaGlzIGlzIG5vcm1hbC4NCg0KV2Ugd2lsbCBvYnRhaW4gZHVwbGljYXRlZCByb3dzIHRoYXQgYXJlIG5vdCB0b3RhbGx5IGlkZW50aWNhbC4gDQpHcm91cGluZyBieSB0aGUgSUQsIHdlIGNhbiBjb3VudCB0aGUgbnVtYmVyIG9mIHJvd3MgcGVyIElELiANCg0KPHU+Tk9URSBJTVBPUlRBTlQ8L3U+DQo+ICEgRk9SIE5PVyBPTiAtIGh1bWFuX2RhdGFfc2VsZWN0aW9uX2RlZHVwICB3aWxsIGJlIHRoZSB2ZXJzaW9uIG9mIHRoZSBkYXRhIHdlDQp3aWxsIGJlIHdvcmtpbmcgb24gKHRoaXMgYWxsb3dzIHVzIG5vdCB0byBoYXZlIHRvIHJ1biBldmVyeXRoaW5nIGFnYWluIGlmIHdlIG1ha2UgYQ0KbWlzdGFrZS4gVGhlbiB3ZSBjYW4gb25seSBydW4gYWdhaW4gc2luY2UgdGhlIGNyZWF0aW9uIG9mIGh1bWFuX2RhdGFfc2VsZWN0aW9uX2RlZHVwKQ0KDQpgYGB7cn0NCiMgY2hhbmdpbmcgdGhlIHR5cGUgb2YgdGhlIElEIHRvIGNoYXJhY3Rlcg0KaHVtYW5fZGF0YV9zZWxlY3Rpb25fZGVkdXAgPC0gDQogIGh1bWFuX2RhdGFfc2VsZWN0aW9uX2RlZHVwICU+JQ0KICBtdXRhdGVfYXQodmFycyhJTklLQV9PSF9UWl9JRCksIGFzLmNoYXJhY3RlcikgJT4lDQogICMgb3JkZXIgLSB0cmljayB0byB1c2UgdGhlIElEIGFzIGEgbnVtYmVyDQogIGFycmFuZ2UoYXMuaW50ZWdlcihJTklLQV9PSF9UWl9JRCkpDQoNCiMgcmVtb3ZpbmcgZXZlbnR1YWwgZHVwbGljYXRlZCByb3dzIA0KZHVwbGljYXRlZF9kYXRhIDwtIA0KICBodW1hbl9kYXRhX3NlbGVjdGlvbl9kZWR1cCAlPiUNCiAgIyBncm91cGluZyBkYXRhIHRoYXQgaGF2ZSBzYW1lIGlkcyANCiAgZ3JvdXBfYnkoSU5JS0FfT0hfVFpfSUQpICU+JQ0KICAjIFRoaXMgYWRkIGEgY29sdW1uIG9mIGNvdW50ICAtIHRoZSBjb3VudCBjb3VudHMgQlkgR1JPVVANCiAgbXV0YXRlKGNvdW50ID0gbigpLCAuYWZ0ZXIgPSAiSU5JS0FfT0hfVFpfSUQiKSAlPiUNCiAgIyBUaGlzIGZpbHRlciBvdXQgdGhlIHJvd3MgdGhhdCBhcmUgbm90IHVuaXF1ZQ0KICBmaWx0ZXIoY291bnQgPiAxKSANCiAgDQpkdXBsaWNhdGVkX2RhdGEgICU+JSBWaWV3KCkNCmBgYA0KDQojIyMjIEV4ZXJjaXNlOiBVbmRlcnN0YW5kaW5nIGdyb3VwX2J5DQoNCldlIHByZXBhcmUgdGhlIGNvbW1vbiBwYXJ0IGZvciB0aGUgdGVzdC4gQXMgZG9uZSBhYm91dmUsIGJ1dCBiZWZvcmUgdGhlIGdyb3VwIGJ5Lg0KYGBge3IgLCBjbGFzcy5zb3VyY2UgPSAiZm9sZC1oaWRlIn0NCnRlc3QgPC0gDQogIGh1bWFuX2RhdGFfc2VsZWN0aW9uX2RlZHVwICU+JQ0KICBtdXRhdGVfYXQodmFycyhJTklLQV9PSF9UWl9JRCksIGFzLmNoYXJhY3RlcikgJT4lDQogIGFycmFuZ2UoYXMuaW50ZWdlcihJTklLQV9PSF9UWl9JRCkpIA0KYGBgDQoNCkdyb3VwIGJ5IGdlbmRlciANCmBgYHtyICwgY2xhc3Muc291cmNlID0gImZvbGQtaGlkZSJ9DQp0ZXN0ICU+JQ0KICB1bmdyb3VwKCkgJT4lDQogIGdyb3VwX2J5KEdlbmRlcikgJT4lDQogICMgVGhpcyBhZGQgYSBjb2x1bW4gb2YgY291bnQgDQogIG11dGF0ZShjb3VudCA9IG4oKSwgLmFmdGVyID0gIklOSUtBX09IX1RaX0lEIikgDQpgYGANCg0KVGhlIGNvdW50IGlzIG5vdyBhY3R1YWxseSBpZGVudGljYWwgdG8gdGhlIG51bWJlciBvZiBtYWxlcyBhbmQgZmVtYWxlcy4gDQpCZWNhdXNlIHdlIGFkZGVkIHRoZSBjb2x1bW4gY291bnQgYXMgd2UgZGlkLCB0aGlzIGNvdW50IGFwcGVhcnMgaW4gZWFjaCBjb2x1bW4uDQoNCk9uZSB3YXkgKGZ1bmN0aW9uYWwgYnV0IG5vdCB0aGUgYmVzdCkgdG8gY3JlYXRlIGEgY29udGluZ2VuY3kgdGFibGUgOiANCg0KYGBge3J9DQp0ZXN0ICU+JQ0KICB1bmdyb3VwKCkgJT4lDQogIGdyb3VwX2J5KEdlbmRlcikgJT4lDQogICMgVGhpcyBhZGQgYSBjb2x1bW4gb2YgY291bnQgDQogIG11dGF0ZShjb3VudCA9IG4oKSwgLmFmdGVyID0gIklOSUtBX09IX1RaX0lEIikgJT4lDQogIHNlbGVjdChHZW5kZXIsIGNvdW50KSAlPiUgDQogIGRpc3RpbmN0KCkNCmBgYA0KDQpUaGlzIGdpdmVzIHVzZSB0aGUgbnVtYmVyIG9mIGZlbWFsZXMgYW5kIG1hbGVzLiANCg0KDQpUaGlzIHRlY2huaXF1ZSBvZiBncm91cGluZyBjYW4gYmVjb21lIHBvd2VyZnVsLCB3aGVuIHlvdSB3YW50IHRvIGNyZWF0ZSBjb250aW5nZW5jeQ0KdGFibGVzIHdoZXJlIHlvdSBjb3VudCBpbnRlcmFjdGlvbnMgYmV0d2VlbiBncm91cHMuDQoNCmBgYHtyfQ0KaHVtYW5fZGF0YV9zZWxlY3Rpb25fZGVkdXAgJT4lIA0KICBzZWxlY3QoU2FtcGxlLCBTZWFzb24sIERpc3RyaWN0KSAlPiUNCiAgYXJyYW5nZShTYW1wbGUsIFNlYXNvbiwgRGlzdHJpY3QpICU+JQ0KICBncm91cF9ieShTYW1wbGUsIFNlYXNvbiwgRGlzdHJpY3QpICU+JQ0KICBtdXRhdGUoY291bnQgPSBuKCkpICU+JQ0KICBkaXN0aW5jdCgpDQpgYGANCg0KPiBIZXJlIGlzIG5vcm1hbCB0aGF0IHdlIGhhdmUgb25lIHdpdGggTkEgTkEgTkEsIGJlY2F1c2Ugd2UgZGlkIG5vdCBmaW5pc2ggdGhlIGRhdGEgY2xlYW5pbmcuDQoNCg0KIyMjIyBGaW5kaW5nIHdoaWNoIHVuaXF1ZSBJRCBhcmUgZHVwbGljYXRlZCANCg0KLi4uIGFuZCB3aGljaCBzb21lIHJvd3MgYXJlIGRpZmZlcmVudCBiZXdlZW4gZHVwbGljYXRlcyANCg0Kd2UgY2FuIGdldCBhIGxpc3Qgb2YgdW5pcXVlIGlkZW50aWZpZXIgaW4gdGhvc2UgdGhhdCBhcmUgZHVwbGljYXRlZCwgdGhlbiB3ZSBjYW4gY2hlY2sgb25lIG15IG9uZQ0KDQpgYGB7cn0NCnVuaXF1ZShkdXBsaWNhdGVkX2RhdGEkSU5JS0FfT0hfVFpfSUQpDQpgYGANCg0KPCEtLQ0KMjExICAyMzc0IDIzMTQzDQotLT4NCg0Kd2UgZmlsdGVyIG91dCB0aGUgaW5jb21wbGV0ZSBkYXRhIC0gdG8gZG8gdGhpcywgd2UgbmVlZCB0byBpZGVudGlmeSB3aGljaCBjb2x1bW4NCmEgd2F5IHRvIGlkZW50aWZ5IGl0IChleGFtcGxlIElEIGFuZCB3aGF0IGlzIG1pc3NpbmcgaW4gdGhlIG1vc3QgaW5jb21wbGV0ZSBhbmQgbm90IG9uIHRoZSBvdGhlcikNCmBgYHtyfQ0KZHVwbGljYXRlZF9kYXRhICU+JSANCiAgZmlsdGVyKElOSUtBX09IX1RaX0lEID09ICIyMTEiKSAlPiUgDQpwcmludCh3aWR0aCA9IEluZikNCmBgYA0KDQo+IE5vdGUgaGVyZSBJIHVzZSAiMjExIiBiZWNhdXNlIElEIGlzIGEgdHlwZSBjaGFyYWN0ZXIuIElmIGl0IHdhcyBhIG51bWJlciBJIHdvdWxkIGhhdmUgdXNlZCAyMTEgd2l0aG91dCB0aGUgcXVvdGVzLiBFcnJvcnMgY2FuIGhhcHBlbiBpZiB5b3UgdXNlIGEgbnVtYmVyIGluc3RlYWQgb2YgY2hhcmFjdGVyIG9yIHZpY2UvdmVyc2EuDQoNCldob19pc195b3VyX2NhcmV0YWtlciBpcyBOQSBpbiB0aGUgcm93IHdlIHdhbnQgdG8gc3VwcHJlc3MgKGxlc3MgY29tcGxldGUpLiANClRoZXJlIGlzIG5vIGluY29tcGF0aWJpbGl0eSBiZXR3ZWVuIHJvd3MgZm9yIHRoaXMgc2FtcGxlIElELiANCg0KV2UgZmlyc3QgY2hlY2sgdGhhdCBvdXIgY29kZSBpcyB3b3JraW5nIG9uIHRoZSBkdXBsaWNhdGVkX2RhdGEgDQp0aGVuIHdoZW4gd2UgYXJlIHN1cmUgaXRzIG9rLCB3ZSBjYW4gZG8gdGhhdCBvbiB0aGUgaHVtYW5fZGF0YV9zZWxlY3Rpb25fZGVkdXANCg0KLSBXZSBzZWxlY3QgdGhlIHJvdyB0aGF0IGlzIG5vdCBjb21wbGV0ZQ0KDQpgYGB7cn0NCmR1cGxpY2F0ZWRfZGF0YSAlPiUgDQogIGZpbHRlcihJTklLQV9PSF9UWl9JRCA9PSAiMjExIiAmIGlzLm5hKFdob19pc195b3VyX2NhcmV0YWtlcikpICU+JSANCiAgcHJpbnQod2lkdGggPSBJbmYpICMgeW91IGNhbiBjaGFuZ2UgZm9yIHZpZXcgaWYgaXRzIG1vcmUgY29udmVuaWVudA0KYGBgDQoNCi0gVG8gZmlsdGVyIG91dCwgd2UgbmVlZCB0byB1c2UgdGhlIE9QUE9TSVRFIG9mIHRoZSBjb25kaXRpb24gd2UgdXNlZCB0byBzZWxlY3QgdGhlIHJvdy4NClRoaXMgaXMgd2hlcmUgd2UgdXNlIGAhYCBpbiBmcm9udCBvZiB0aGUgV0hPTEUgY29uZGl0aW9uIG9mIHNlbGVjdGlvbjsgdGhlIHBvc2l0aW9uIA0Kb2YgYnJhY2tldHMgYXJlIGltcG9ydGFudC4gDQoNCmBgYHtyfQ0KaHVtYW5fZGF0YV9zZWxlY3Rpb25fZGVkdXAgPC0gDQogIGh1bWFuX2RhdGFfc2VsZWN0aW9uX2RlZHVwICU+JQ0KICBmaWx0ZXIoISAoSU5JS0FfT0hfVFpfSUQgPT0gIjIxMSIgJiBpcy5uYShXaG9faXNfeW91cl9jYXJldGFrZXIpKSApDQoNCiMgQ29udHJvbCB0aGF0IGl0cyB0aGUgY29ycmVjdCB2YWx1ZSBpcyB0aGVyZSBhbmQgdGhhdCB0aGVyZSBhcmUgbm8gZHVwbGljYXRlcw0KaHVtYW5fZGF0YV9zZWxlY3Rpb25fZGVkdXAgJT4lIGZpbHRlcihJTklLQV9PSF9UWl9JRCA9PSAyMTEpDQpgYGANCg0KPiBOb3RlIDogaWYgeW91IGRvIGVycm9ycywgeW91IHdpbGwgaGF2ZSB0byByZXJ1biBldmVyeXRoaW5nIHVwIHRvIHRoaXMNCnBvaW50ICEgDQoNCg0KTm93IHdlIGNhbiByZXBlYXQgdGhvc2Ugc3RlcHMgZm9yIHRoZSBvdGhlciBkdXBsaWNhdGVkIGRhdGENCmBgYHtyfQ0KZHVwbGljYXRlZF9kYXRhICU+JSANCiAgZmlsdGVyKElOSUtBX09IX1RaX0lEID09ICIyMzc0IikgJT4lIA0KICBwcmludCh3aWR0aCA9IEluZikNCmBgYA0KDQpIZXJlLCB3ZSBzZWUgd2UgaGF2ZSBhIHByb2JsZW0uIFRoZSB0d28gcmF3cyBvZiBkYXRhIGFyZSBpbmNvbXBhdGlibGUuIA0KSWYgd2UgY2FuIGZpbmQgb3V0IHdoYXQgZGF0YSBpcyBjb3JyZWN0LCB0aGVuIHdlIG5lZWQgdG8gcmVjdGlmeSB0aGUgZGF0YS4NClRoaXMgaXMgZG9uZSBieSBzY3JpcHQgLSBzbyB3ZSBrbm93IGl0IGhhcyBiZWVuIGRvbmUgKGNoYW5nZXMgaW4gdGhlIHJhdyBkYXRhDQphcmUgbm90IGRlc2lyYWJsZSkuIA0KDQoqKkFzc3VtaW5nIHRoYXQgVGhlIHJlY29yZCB3aXRoIHRoZSBGZW1hbGUgaXMgY29ycmVjdCB3ZSBjYW4gcmVtb3ZlIHRoZSByb3cgd2l0aCANCnRoZSBNYWxlLioqIA0KDQotIEZpcnN0IEkgbWFrZSBzdXJlIEkgc2VsZWN0IHRoZSBjb3JyZWN0IHJvdyANCmBgYHtyfQ0KIyBTZWxlY3RpbmcgdGhlIGNvbHVtbiB0aGF0IHdlIHdhbnQgdG8gcmVtb3ZlDQpkdXBsaWNhdGVkX2RhdGEgJT4lIA0KICBmaWx0ZXIoSU5JS0FfT0hfVFpfSUQgPT0gIjIzNzQiICYgR2VuZGVyID09ICJNYWxlIikgJT4lIA0KICBwcmludCh3aWR0aCA9IEluZikNCmBgYA0KDQotIFRoZW4gSSByZW1vdmUgdGhlIHJvdyBmcm9tIHRoZSBkYXRhIGJ5IGNob29zaW5nIHRoZSBPUFBPU0lURQ0KYGBge3J9DQojIFJlbW92aW5nIHRoZSBjb2x1bW4gLSB3ZSBmaWx0ZXIgYnkgdGhlIG5lZ2F0aW9uIG9mIHRoZSBjb2x1bW4gd2Ugd2FudCB0byByZW1vdmUNCmh1bWFuX2RhdGFfc2VsZWN0aW9uX2RlZHVwIDwtIA0KICBodW1hbl9kYXRhX3NlbGVjdGlvbl9kZWR1cCAlPiUNCiAgZmlsdGVyKCEgKElOSUtBX09IX1RaX0lEID09ICIyMzc0IiAmIEdlbmRlciA9PSAiTWFsZSIpKSANCg0KIyBWZXJpZmljYXRpb24gdGhhdCB0aGUgb3RoZXIgZGF0YSBoYXMgYmVlbiByZW1vdmVkIA0KaHVtYW5fZGF0YV9zZWxlY3Rpb25fZGVkdXAgJT4lDQogIGZpbHRlcihJTklLQV9PSF9UWl9JRCA9PSAiMjM3NCIpIA0KDQpgYGANCg0KPiB5b3UgY2FuIHNlZSB0aGF0IHZlcmlmeWluZyB5b3VyIGRhdGEgd2l0aCBjb2RlLCBhbGxvd3MgeW91IHRvIHRyYWNrIHdoYXQgeW91IGhhdmUgZG9uZS4NCkl0IGFsbG93cyB5b3UgYWxzbyB0byBkb2N1bWVudCB5b3VyIGNob2ljZXMsIG9mIHdoYXQgdGhpcyB3YXMgcmVtb3ZlZCwgYW5kIGlmIHlvdSBkaWQgDQphIG1pc3Rha2UgeW91IGNhbiBmaXggaXQgYW5kIHJlcnVuIHRoZSBjb2RlLiBUaGUgcmF3IGRhdGEgaXMgbm90IGRpc3Ryb3llZCwgc28geW91IGNhbg0KcmVydW4gdGhlIGNvZGUgdW50aWwgeW91IGFyZSBzYXRpc2ZpZWQgeW91IGhhdmUgZm91bmQgb3V0IGFsbCB0aGUgZGF0YSB3aGVyZSBxdWFsaXR5IA0Kd2FzIG5vdCBnb29kLiAgDQoNCiMjIyMgRXhlcmNpc2UgKHdlIGRvIG5vdCBkbyBkdXJpbmcgdGhlIGNvdXJzZSkNCg0KDQpUaGVuIHdlIG5lZWQgdG8gY29udGludWUgdGhlIGxpc3Qgb2YgSURzIHdoZXJlIHdlIGRldGVjdGVkIGRhdGEgdGhhdCB3YXMNCnBvdGVudGlhbGx5IGluY29ycmVjdCAoaGVyZSB3ZSBzdGlsbCBoYXZlIG9uZTogSUQgMjMxNDMpDQoNCmBgYHtyICwgY2xhc3Muc291cmNlID0gImZvbGQtaGlkZSJ9DQpkdXBsaWNhdGVkX2RhdGEgJT4lIA0KICBmaWx0ZXIoSU5JS0FfT0hfVFpfSUQgPT0gIjIzMTQzIikgJT4lIA0KICBwcmludCh3aWR0aCA9IEluZikNCmBgYA0KUmVtb3ZlIHRoZSByYXcgdGhhdCBpcyBmYWxzZSAoaWYgeW91IGRvIG5vdCBrbm93LCBhc3N1bWUgd2hpY2ggb25lIGlzIGZhbHNlDQpmb3IgdGhlIHNha2Ugb2YgdGhlIGV4ZXJjaXNlKQ0KDQotIHNlbGVjdCB0aGUgcmF3IHdpdGggdGhlIHdyb25nIGRhdGENCmBgYHtyICwgY2xhc3Muc291cmNlID0gImZvbGQtaGlkZSJ9DQpkdXBsaWNhdGVkX2RhdGEgJT4lIA0KICBmaWx0ZXIoSU5JS0FfT0hfVFpfSUQgPT0gIjIzMTQzIiAmIEhhdmVfeW91X2V2ZXJfaGVhcmRfYWJvdXRfQU1SID09ICJObyIpICU+JSANCiAgcHJpbnQod2lkdGggPSBJbmYpDQpgYGANCi0gU2VsZWN0IHRoZSBvcHBvc2l0ZSB0byBhbmQgcmVtb3ZlIHRoZSByYXcgd2l0aCB0aGUgd3JvbmcgZGF0YSBmcm9tIHRoZSBkYXRhIHNldCANCg0KYGBge3IgLCBjbGFzcy5zb3VyY2UgPSAiZm9sZC1oaWRlIn0NCmh1bWFuX2RhdGFfc2VsZWN0aW9uX2RlZHVwIDwtIA0KICBodW1hbl9kYXRhX3NlbGVjdGlvbl9kZWR1cCAlPiUNCiAgZmlsdGVyKCEgKElOSUtBX09IX1RaX0lEID09ICIyMzE0MyIgJiBIYXZlX3lvdV9ldmVyX2hlYXJkX2Fib3V0X0FNUiA9PSAiTm8iKSkNCmBgYA0KDQotIFZlcmlmaWNhdGlvbiB3ZSBzdGlsbCBoYXZlIHRoZSBjb3JyZWN0IGRhdGEgDQpgYGB7ciAsIGNsYXNzLnNvdXJjZSA9ICJmb2xkLWhpZGUifQ0KaHVtYW5fZGF0YV9zZWxlY3Rpb25fZGVkdXAgJT4lDQogIGZpbHRlcihJTklLQV9PSF9UWl9JRCA9PSAiMjMxNDMiKQ0KYGBgDQoNCi0tLS0NCg0KIyMjIEEgQ2xvc2VyIGluc3BlY3Rpb24gb2YgbWlzc2luZyB2YWx1ZXMgLSBGaW5kaW5nIG91dCB0byB3aXRoIElkZW50aWZpZXIgaXQgY29ycmVzcG9uZHMuDQoNCi0gaGVyZSB3ZSBvbmx5IHNlbGVjdCBlZy4gc29tZSBjb2x1bW5zIHRoYXQgd2Uga25vdyB3ZSBjYW4gdmVyaWZ5DQpgYGB7cn0NCmNvbG5hbWVzKGh1bWFuX2RhdGFfc2VsZWN0aW9uX2RlZHVwKQ0KDQpzdWJzZXRfTkEgPC0gDQogIGh1bWFuX2RhdGFfc2VsZWN0aW9uX2RlZHVwICU+JQ0KICBzZWxlY3QoYyhJTklLQV9PSF9UWl9JRCxXaGljaF9jbGFzc19ncmFkZV9hcmVfeW91LCBXaG9faXNfeW91cl9jYXJldGFrZXIpKSAlPiUgIyB3ZSBtYWRlIGEgc3Vic2V0IG9mIHRoZSBkYXRhZnJhbWUgDQogIGZpbHRlcihpZl9hbnkoZXZlcnl0aGluZygpLCBpcy5uYSkpICAjIGV2ZXJ5dGhpbmcgbWVhbnMgYWxsIHRoZSBjb2x1bW5zIGluIHRoZSBkYXRhIGZyYW1lDQoNCnN1YnNldF9OQQ0KYGBgDQoNCldlIGhhdmUgY3JlYXRlZCBhIHN1YnNldCBvZiBkYXRhIHdoZXJlIGNvbHVtbnMgaGF2ZSBtaXNzaW5nIHZhbHVlcy4gDQoNCldlIGNhbiBzYXZlIHRoaXMgc3Vic2V0IGludG8gYSBzcHJlYXNoZWV0IHNvIHlvdSBjYW4uIGVnLiBwcmludCB0aGlzIGRhdGEgZnJhbWUNCmlmIHlvdSBuZWVkIHRvIGdvIGJhY2sgdG8gc29tZSBub3RlcyAoZWcuIGxhYiBub3RlcyBvbiBwYXBlcikgdG8gdmVyaWZ5IHRob3NlIA0KdmFsdWVzLiANCg0KYGBge3J9DQp3cml0ZV9jc3Yoc3Vic2V0X05BLCBoZXJlOjpoZXJlKCJyZXN1bHRzIiwgInN1YnNldF9OQS5jc3YiKSkNCmBgYA0KDQoNClNob3VsZCB5b3UgcmVwbGFjZSBmZXcgbWlzc2luZyB2YWx1ZXMsIGVnLiBpZiB5b3UgaGFkIGZvcmdvdHRlbiB0byByZWdpc3RlciANCmFuZCBpZiBpdCB3YXMgb24gYSBwYXBlciBub3RlLCB5b3UgY2FuIGRvIHRoYXQgYnkgc2VsZWN0aW5nIHRoZSBjZWxsIHdoZXJlIHRoZSANCnZhbHVlIHNob3VsZCBiZSBhbmQgdGhlbiBhc3NpZ25pbmcgaXQuIA0KDQpJbiBvdXIgZXhhbXBsZSB3ZSB1c2UgdGhlIHN1YnNldF9OQSB0byBzaG93IHRoYXQsIGJ1dCBpZiB5b3UgaGFkIHRvIGNoYW5nZSB0aGUNCnZhbHVlcyB0aGV5IHNob3VsZCBnbyBpbnRvIHRoZSBodW1hbl9kYXRhX3NlbGVjdGlvbiwgd2hpY2ggaXMgdGhlIGNsZWFuZWQgZGF0YQ0KdGhhdCB3aWxsIGJlIHVzZWQgbGF0ZXIgb24gZm9yIGFuYWx5c2lzLiANCg0KMS4gRmluZGluZyB0aGUgY2VsbCB3ZSB3YW50IHRvIGNoYW5nZSBlZzogSUQgMjMxICBhbmQgdGhlIGdyYWRlLiANCkhlcmUgd2UgZG8gYW4gZXhhbXBsZSBvbiB0aGUgc3Vic2V0LCB3ZSBkbyBub3QgY2hhbmdlIHRoZSBkYXRhLiANCg0KV2Ugc2hvdyB5b3UgaG93IGl0IGNhbiBiZSBkb25lIElGIHlvdSBjYW4gcmVjb3ZlciB0aGUgbWlzc2luZyBkYXRhIGZyb20geW91ciBub3Rlcy4gDQoNCi0geW91IGNhbiB1c2UgbXV0YXRlIGFuZCBpZl9lbHNlIDogY29uZGl0aW9uLCB2YWx1ZSBjb25kaXRpb24gdHJ1ZSwgdmFsdWUgY29uZGl0aW9uIGZhbHNlDQpgYGB7cn0NCnN1YnNldF9OQSAlPiUgDQogIG11dGF0ZShXaGljaF9jbGFzc19ncmFkZV9hcmVfeW91ID0gDQogICAgICAgICAgIGlmX2Vsc2UoSU5JS0FfT0hfVFpfSUQgPT0gIjIzMSIsICJHcmFkZSAxMCIsIFdoaWNoX2NsYXNzX2dyYWRlX2FyZV95b3UpKSAlPiUgDQogIHByaW50KHdpZHRoID0gSW5mKQ0KYGBgDQoNCi0gQSB2YXJpYW50IG9mIGlmX2Vsc2Ugd2hlbiB5b3UgbXVzdCByZXBsYWNlIHNldmVyYWwgdmFsdWVzDQoNCmBgYHtyfQ0Kc3Vic2V0X05BICU+JSANCiAgbXV0YXRlKFdoaWNoX2NsYXNzX2dyYWRlX2FyZV95b3UgPSANCiAgICAgICAgICAgY2FzZV93aGVuKA0KICAgICAgICAgICAgIElOSUtBX09IX1RaX0lEID09ICIyMzEiIH4gIkdyYWRlIDEwIiwNCiAgICAgICAgICAgICBJTklLQV9PSF9UWl9JRCA9PSAiMjMzIiB+ICJHcmFkZSAxMCIsDQogICAgICAgICAgICAgVFJVRSB+IFdoaWNoX2NsYXNzX2dyYWRlX2FyZV95b3UNCiAgICAgICAgICAgKSkgJT4lIA0KICBwcmludCh3aWR0aCA9IEluZikNCmBgYA0KDQotIHdoZW4gdHJ1ZSBpdCBkb2VzIHRoYXQgZm9yIHRoZSBsaW5lLiANCi0gdGhlIHRydWUgYXQgdGhlIGVuZCwgaXMgZXZlcnl0aGluZyB0aGF0IHdhcyBub3QgdHJ1ZSBiZWZvcmUuIA0KDQpCZSBjYXJlZnVsIGlmIHlvdSB1c2UgdGhhdCwgdGhlIG9yZGVyIG1pZ2h0IG1hdHRlciBpZiB5b3UgdXNlIGl0IGluIGEgDQpjb21wbGljYXRlZCB3YXkhIA0KDQoNCiMjIyBPdGhlciBzdW1tYXJ5IHN0YXRpc3RpY3MgLi4uDQoNCkV4YW1wbGUgeW91IHdhbnQgdG8ga25vdyBob3cgbWFueSBjaGlsZHJlbiBvZiANCmRpZmZlcmVudCBhZ2VzIHlvdSBoYXZlIGluIHlvdXIgZGF0YSBzZXQNCg0KDQoNCmBgYHtyfQ0KZ2xpbXBzZShodW1hbl9kYXRhX3NlbGVjdGlvbl9kZWR1cCkNCmBgYA0KDQpIZXJlIGlzIHRoZSBmYXN0IHRvIGNvdW50IGhvdyBtYW55IGNoaWxkcmVuIG9mIGRpZmZlcmVudCBhZ2VzIHlvdSBoYXZlIGluIHlvdXIgZGF0YSBzZXQNCmBgYHtyfQ0KaHVtYW5fZGF0YV9zZWxlY3Rpb25fZGVkdXAgJT4lIA0KICBncm91cF9ieShBZ2VfX3lycykgJT4lDQogIHN1bW1hcmlzZShuID0gbigpKQ0KYGBgDQoNClRoaXMgaXMgdGhlIGxvbmcgd2F5IHRvIGRvIHRoZSBzYW1lIHRoaW5nIC4uLiB3ZSBoYWQgZG9uZSBsaWtlIHRoYXQgYmVmb3JlLiANCmBgYHtyfQ0KaHVtYW5fZGF0YV9zZWxlY3Rpb25fZGVkdXAgJT4lIA0KICBncm91cF9ieShBZ2VfX3lycykgJT4lDQogIG11dGF0ZShjb3VudCA9IG4oKSkgJT4lDQogIHNlbGVjdChBZ2UgOj0gQWdlX195cnMsIGNvdW50KSAlPiUgIyA6PSBhbGxvd3MgdG8gY2hhbmdlIHRoZSBuYW1lIG9mIHRoZSB2YXJpYWJsZSB3aGlsZSB1c2luZyBpdCAocHJhY3RpY2FsICEpIA0KICBhcnJhbmdlKEFnZSkgJT4lDQogIGRpc3RpbmN0KCkgDQoNCmBgYA0KDQoNCllvdSBjYW4gYWxzbyBvcHRhaW4gc3VtbWFyeSBzdGF0aXN0aWNzDQplZy4gdGhlIG1lYW4gYWdlIG9mIHRoZSBjaGlsZHJlbiwgdGhlIHF1YW50aWxlcyBvZiB0aGUgYWdlIGRpc3RyaWJ1dGlvbiBpbiB0aGUgZGF0YSwNCmFzIHdlbGwgYXMgdGhlIG1pbiBhbmQgbWF4LiANCg0KYGBge3J9DQpodW1hbl9kYXRhX3NlbGVjdGlvbl9kZWR1cCAlPiUgDQogIHN1bW1hcmlzZShtZWFuX2FnZSA9IG1lYW4oQWdlX195cnMsIG5hLnJtID0gVFJVRSksDQogICAgICAgICAgICBtZWRpYW5fYWdlID0gbWVkaWFuKEFnZV9feXJzLCBuYS5ybSA9IFRSVUUpLA0KICAgICAgICAgICAgcXVhbnRpbGVfMjUgPSBxdWFudGlsZShBZ2VfX3lycywgcHJvYnMgPSAwLjI1LCBuYS5ybSA9IFRSVUUpLA0KICAgICAgICAgICAgcXVhbnRpbGVfNzUgPSBxdWFudGlsZShBZ2VfX3lycywgcHJvYnMgPSAwLjc1LCBuYS5ybSA9IFRSVUUpLA0KICAgICAgICAgICAgbWluX2FnZSA9IG1pbihBZ2VfX3lycywgbmEucm0gPSBUUlVFKSwNCiAgICAgICAgICAgIG1heF9hZ2UgPSBtYXgoQWdlX195cnMsIG5hLnJtID0gVFJVRSkpDQpgYGANCg0KUmVtZW1iZXIgaW4gdGhlIGJlZ2lubmluZyB3ZSB1c2VkIHRoZSBzdW1tYXJ5IGZ1bmN0aW9uID8gdGhpcyBjYW4gYWxzbyBiZSBvYnRhaW5lZCBsaWtlIHRoYXQuDQpUaGVyZSBhcmUgYWR2YW50YWdlcyBhbmQgaW5jb252ZW5pZW50IGluIGJvdGggbWV0aG9kcy4gDQpXaXRoIHN1bSBhcmlzZSwgSSBjYW4gZGVmaW5lIGV4YWN0bHkgd2hhdCBJIHdhbnQgdG8gc2VlLCBhbmQgaG93IGl0IHNob3VsZCBiZSBjYWxjdWxhdGVkLiANCk1vcmVvdmVyIHRoaXMgcHJvZHVjZSBhIGRhdGEgZnJhbWUgdGhhdCBJIGNhbiBleHBvcnQNCg0KDQpgYGB7cn0NCmh1bWFuX2RhdGFfc2VsZWN0aW9uX2RlZHVwICU+JSANCiAgc2VsZWN0KEFnZV9feXJzKSAlPiUNCiAgc3VtbWFyeSgpDQpgYGANCg0KDQpTdW1tYXJpemUgaXMgdmVyeSBwb3dlcmZ1bCB0byBjYWxjdWxhdGUgc2ltcGxlIGNvbnRpbmdlbmN5IHRhYmxlcyBmb3IgZ3JvdXBzLiANCmBgYHtyfQ0KaHVtYW5fZGF0YV9zZWxlY3Rpb25fZGVkdXAgJT4lIA0KICBncm91cF9ieShHZW5kZXIpICU+JQ0KICBzdW1tYXJpc2UobWVhbl9hZ2UgPSBtZWFuKEFnZV9feXJzLCBuYS5ybSA9IFRSVUUpLA0KICAgICAgICAgICAgbWVkaWFuX2FnZSA9IG1lZGlhbihBZ2VfX3lycywgbmEucm0gPSBUUlVFKSwNCiAgICAgICAgICAgIHF1YW50aWxlXzI1ID0gcXVhbnRpbGUoQWdlX195cnMsIHByb2JzID0gMC4yNSwgbmEucm0gPSBUUlVFKSwNCiAgICAgICAgICAgIHF1YW50aWxlXzc1ID0gcXVhbnRpbGUoQWdlX195cnMsIHByb2JzID0gMC43NSwgbmEucm0gPSBUUlVFKSwNCiAgICAgICAgICAgIG1pbl9hZ2UgPSBtaW4oQWdlX195cnMsIG5hLnJtID0gVFJVRSksDQogICAgICAgICAgICBtYXhfYWdlID0gbWF4KEFnZV9feXJzLCBuYS5ybSA9IFRSVUUpLA0KICAgICAgICAgICAgLmJ5X2dyb3VwID0gVFJVRSkNCmBgYA0KDQpBbm90aGVyIGV4YW1wbGUgOiANCg0KYGBge3J9DQpodW1hbl9kYXRhX3NlbGVjdGlvbl9kZWR1cCAlPiUgDQogIGdyb3VwX2J5KEhhdmVfeW91X2V2ZXJfaGVhcmRfYWJvdXRfQU1SLCBJZl95ZXNfX2hvd19kaWRfeW91X2dldF90aGlzX2luZm9ybWF0aW9uKSAlPiUNCiAgc3VtbWFyaXNlKG4gPSBuKCkpDQoNCmBgYA0KDQpUaGlzIGlzIGEgd2F5IHRvIGNvbnRyb2wgaWYgdGhlIGRhdGEgaXMgY29uc2lzdGVudC4gSWYgcGVvcGxlIHdvdWxkIGhhdmUgYm90aA0KYW5zd2VyZWQgdGhhdCB0aGV5IGhhdmUgbm90IGhlYXJkIGFib3V0IEFNUiBidXQgaWYgdGhleSBoYWQgZ2l2ZW4gYSBwcmVjaXNpb24gb24gDQp3aGVyZSwgdGhlbiBpdCBtaWdodCBpbmRpY2F0ZSBlcnJvciBpbiBkYXRhIHJlY29yZGluZywgb3IgdGhhdCBwZW9wbGUgbWF5YmUgZGlkIA0Kbm90IGFuc3dlciBwcm9wZXJseSB0byB0aGUgcXVlc3Rpb25uYWlyZS4gDQoNClNvbWUgcXVlc3Rpb25uYWlyZXMgaGF2ZSByZWR1bmRhbnQNCmFuZCBjb250cm9sIHF1ZXN0aW9ucywgb25seSBmb3JtdWxhdGVkIGluIGEgZGlmZmVyZW50IHdheXMsIHRvIGNoZWNrIGZvciB0aGUNCmNvbnNpc3RlbmN5IG9mIHRoZSBkYXRhLiANCg0KRW5zdXJpbmcgdGhlIGRhdGEgcXVhbGl0eSwgbWVhbnMgdGhhdCB5b3UgaGF2ZSB0byB0aGluayBvZiBhbGwgdGhlIHRoaW5ncw0KdGhhdCBjb3VsZCBoYXZlIGdvbmUgd3JvbmcsIGFuZCB0byBjaGVjayBmb3IgdGhlbS4gVGhlIG1vcmUgeW91IGtub3cgeW91ciBkYXRhDQp0aGUgZWFzaWVyIGl0IGdldHMgdG8gdGhpbmsgYWJvdXQgd2hhdCBjb3VsZCBoYXZlIGdvbmUgd3JvbmcuIA0KDQoNCiMgISBNYWRlbGFpbmUgY2hlY2sgKHRvIGJlIHN1cmUgaXRzIHN0aWwgdGhlcmUpDQolIyMjIEV4ZXJjaWNlIDogSSBkaXNjb3ZlcmVkIG1vcmUgZGF0YSB0byByZWNoZWNrIA0KDQpIdW0sIGl0IHNlZW1zIHRvIG1lIHRoYXQgd2UgbWlnaHQgc3RpbGwgaGF2ZSBvbmUgbGluZSB3aXRoIG1hbnkgbWlzc2luZyB2YWx1ZXMgaW4gdGhlIGRhdGFzZXQNCkZpbmQgYSB3YXkgdG8gc2VsZWN0IHRoZSBsaW5lIHdpdGggYSBsb3Qgb2YgTkFzICh5b3UgY2FuIGRvIG1hbnkgc3RlcHMsIGFzIGxvbmcgYXQgeW91IG1hbmFnZSBhdCB0aGUgZW5kKQ0KDQoNCmBgYHtyIGV2YWw9RkFMU0V9DQpodW1hbl9kYXRhX3NlbGVjdGlvbl9kZWR1cCAlPiUNCiAgZmlsdGVyKGlzLm5hKEdlbmRlcikgJiBpcy5uYShBZ2VfX3lycykpDQpgYGANCg0KDQojIyMgQmFzaWMgcGxvdHRpbmcgdG8gY2hlY2sgeW91ciBkYXRhIA0KDQpXZSBjYW4gYWxzbyBleHBsb3JlIG91ciBkYXRhIHZpc3VlbGxlbWVudC4gDQoNClNvbWUgYmFzaWMgcGxvdHMgY2FuIGJlIHVzZWQgZWcuIHRvIGNoZWNrIGNvbnNpc3RlbmNpZXMgYmV0d2VlbiBhbnN3ZXJzLCANCm9yIGNhbiBzZXJ2ZSBhcyBjb250aW5nZW5jeSB0YWJsZXMgDQoNCg0KDQpgYGB7cn0NCmdsaW1wc2UoaHVtYW5fZGF0YV9zZWxlY3Rpb25fZGVkdXApDQpodW1hbl9kYXRhX3NlbGVjdGlvbl9kZWR1cCAlPiUgDQogICAgICAgICAgIGdncGxvdChhZXMoeCA9IEFnZV9feXJzLCBmaWxsID0gR2VuZGVyKSkgKw0KICAgICAgICAgICBnZW9tX2JhcihzdGF0ID0gImNvdW50IiwgcG9zaXRpb24gPSAic3RhY2siKSArDQogICAgICAgICAgIHRoZW1lX21pbmltYWwoKSAgDQpgYGANCg0KDQpJbiB0aGUgcGxvdCBhYm92ZSwgd2UgdXNlZCBmYWN0b3JzLiANClNvbWV0aW1lcyB1c2luZyBmYWN0b3JzIGNhbiBiZSB0cmlja3ksIGlmIHNvbWUgc3RhdGlzdGljcyBhcmUgZG9uZSBvbiB0aGUgdmFsdWVzLg0KU28gYmUgY2FyZWZ1bC4gDQoNCkkgcHJlZmVyIHRvIHRyYW5zZm9ybSBmYWN0b3JzIGludG8gY2hhcmFjdGVycywgdG8gYXZvaWQgYW55IGNvbmZ1c2lvbi4NCmBgYHtyfQ0KZGZfcGxvdDIgPC0gDQogIGh1bWFuX2RhdGFfc2VsZWN0aW9uX2RlZHVwICU+JSANCiAgbXV0YXRlX2lmKGlzLmZhY3RvciAsIGFzLmNoYXJhY3RlcikgJT4lDQogIGZpbHRlcighaXMubmEoR2VuZGVyKSkNCmBgYA0KV2UgY2FuIHJlbW92ZSB0aGUgTkFzICBmb3IgbndvDQoNCi0gc21hbGwgZGlmZmVyZW5jZXMgb2Ygb3B0aW9ucyBjYW4gbWFrZSB5b3VyIHBsb3QgbG9vayB0b3RhbGx5IGRpZmZlcmVudGx5DQoNCmBgYHtyfQ0KZ2dwbG90KGRmX3Bsb3QyLGFlcyh4ID0gQWdlX195cnMsIGZpbGwgPSBHZW5kZXIpKSArDQogICAgICAgICAgIGdlb21fYmFyKHN0YXQgPSAiY291bnQiLCBwb3NpdGlvbiA9ICJzdGFjayIpICsNCiAgICAgICAgICAgdGhlbWVfbWluaW1hbCgpIA0KYGBgDQpTZWUgTkFzIGFyZSBnb25lLiANCllvdSBuZWVkIHRvIHRoaW5rIGlmIHRoZXkgYXJlIHdvcnRoIGFkZGluZyBvciBub3QuIFdoYXQgZG8gdGhleSBtZWFuIGluIHlvdXIgZGF0YSA/IA0KDQpTbWFsbCBjaGFuZ2VzIGNhbiBtYWtlIHlvdXIgcGxvdCBsb29rIHZlcnkgZGlmZmVyZW50Lg0KDQpgYGB7cn0NCmdncGxvdChkZl9wbG90MixhZXMoeCA9IEFnZV9feXJzLCBmaWxsID0gR2VuZGVyKSkgKw0KICAgICAgICAgICBnZW9tX2JhcihzdGF0ID0gImNvdW50IiwgcG9zaXRpb24gPSAic3RhY2siKSArDQogICAgICAgICAgIHRoZW1lX21pbmltYWwoKSArDQogIGZhY2V0X3dyYXAofkdlbmRlcikgKw0KICBsYWJzKHRpdGxlID0gIk51bWJlciBvZiBpbnRlcm9nYXRlZCBwYXJ0aWNpcGFudCBieSBhZ2UgYW5kIEdlbmRlciIsIA0KICAgICAgIHggPSAiQWdlICh5ZWFycykiLA0KICAgICAgIHkgPSAiTnVtYmVyIG9mIHBhcnRpY2lwYW50cyIpIA0KYGBgDQoNCkhlcmUgd2Ugc2hvdyBhbm90aGVyd2F5IHlvdSBjYW4gcmVtb3ZlIE5BcyBpbiB0aGUgZmFjdG9ycw0KDQpgYGB7cn0NCm90aGVyX25vbk5BIDwtIA0KICBodW1hbl9kYXRhX3NlbGVjdGlvbl9kZWR1cCAlPiUgDQogIGZpbHRlcighaXMubmEoR2VuZGVyKSkgJT4lDQogIG11dGF0ZV9pZihpcy5mYWN0b3IsIH5kcm9wbGV2ZWxzKC4pKQ0KDQpwbG90MSA8LSBnZ3Bsb3Qob3RoZXJfbm9uTkEsDQogICAgICAgYWVzKHggPSBBZ2VfX3lycywgZmlsbCA9IEdlbmRlcikpICsNCiAgZ2VvbV9ib3hwbG90KG5hLnJtID0gVFJVRSkgKw0KICB0aGVtZV9taW5pbWFsKCkgKw0KICBmYWNldF93cmFwKH5HZW5kZXIpICsNCiAgbGFicyh0aXRsZSA9ICJEaXN0cmlidXRpb24gb2YgdGhlIGFnZSBvZiBwYXJ0aWNpcGFudHMgYnkgR2VuZGVyIiwgDQogICAgICAgeCA9ICJBZ2UgKHllYXJzKSIsDQogICAgICAgeSA9ICJHZW5kZXIiKSANCnBsb3QxDQpgYGANCg0KV2Ugc2VlIHRoYXQgd2UgaGF2ZSBmZXcgdmFsdWVzIHRoYXQgYXJlIG91dGxpbmVzLiBIZXJlLCBpdHMgcHJvYmFibHkgZHVlwqgNCnRvIHRoZSBmYWN0IHRoYXQgbW9zdCBvZiB0aGUgZGF0YSB3YXMgY29sbGVjdGVkIGF0IHNjaG9vbHMuIA0KDQpXZSBjYW4gY2hlY2sgaWYgdGhpcyBjb3JyZXNwb25kcyB0byBvdXIgcHJldmlvdXMgY29udGluZ2VuY3kgdGFibGUuIA0KSG93ZXZlciB0aGUgb3V0bGllcnMgbWFrZSBpdCBkaWZmaWN1bHQgdG8gc2VlIHRoZSBtZWFuIGFuZCBtZWRpYW4gdmFsdWVzIG9uIHRoZSBncmFwaA0KDQpCeSByZW1vdmluZyB0aGUgb3V0bGllcnMgaW4gdGhlIGRpc3BsYXksIGl0IGlzIGVhc2llciB0byBjb21wYXJlIHRoZSBjZW50cmFsIHZhbHVlcyBvZiB0aGUgZGlzdHJpYnV0aW9pbiwNCmBgYHtyfQ0KcGxvdF9ub3RvdWxpZXJzIDwtIA0KICBnZ3Bsb3Qob3RoZXJfbm9uTkEsDQogICAgICAgYWVzKHggPSBBZ2VfX3lycywgZmlsbCA9IEdlbmRlcikpICsNCiAgZ2VvbV9ib3hwbG90KG5hLnJtID0gVFJVRSwgb3V0bGllcnMgPSBGQUxTRSkgKw0KICB0aGVtZV9taW5pbWFsKCkgKw0KICBmYWNldF93cmFwKH5HZW5kZXIpICsNCiAgbGFicyh0aXRsZSA9ICJEaXN0cmlidXRpb24gb2YgdGhlIGFnZSBvZiBwYXJ0aWNpcGFudHMgYnkgR2VuZGVyIiwgDQogICAgICAgeCA9ICJBZ2UgKHllYXJzKSIsDQogICAgICAgeSA9ICJHZW5kZXIiKSANCg0KcGxvdF9ub3RvdWxpZXJzDQpgYGANCkVnIG1lZGlhbiBhZ2UgdmFsZSB3YXMgOiBmb3IgZmVtYWxlcyB3YXMgMTMgYW5kIDE0IGZvciBtYWxlcw0KDQotIFlvdSBzZWUgdGhhdCB3ZSBjaGFuZ2VkIHRoZSB0eXBlIG9mIHRoZSBwbG90IGJ5IGNoYW5naW5nIHRoZSBnZW9tDQotIHBsb3RzIGFyZSBkb25lIGJ5IGFkZGluZyBkaWZmZXJlbnQgbGF5ZXJzDQotIGFzIHVzdWFsIGFsd2F5cyBoYXZlIGNyaXRpY2FsIHNlbnNlLiANCi0geW91IGNhbiBzdG9yZSBwbG90cyBhcyBvYmplY3RzIGFuZCB2aWV3IHRoZW0gYnkgcmVjYWxsaW5nIHRoZSBvYmplY3QNCi0gc3RvcmluZyBwbG90IGFzIG9iamVjdHMgYWxsb3dzIGV4cGxvcmluZyB0aGVpciBjb250ZW50ICh0aGlzIGFsbG93cyB5b3UgdG8gc2VlIGlmIHRoZSBkYXRhIHdhcyB1c2VkIGNvcnJlY3RseSkNCg0KRmluZGluZyB0aGUgZGF0YSB1c2VkIGluIGEgcGxvdA0KYGBge3J9DQpnbGltcHNlKHBsb3Rfbm90b3VsaWVycykNCmBgYA0KDQpFYWNoIGxheWVyIGluIHRoZSBwbG90IChiYXNpY2FsbHkgYWxsIHRoZSB0aGluZ3MgeW91IGNhbiBtb2RpZnkpIGNhbiBiZSBpbnNwZWN0ZWQNCnVzaW5nIGAkYC4gUGxvdHMgYXJlIGZhc3QgdG8gbWFrZSwgYmVjYXVzZSB0aGVyZSBhcmUgYSBsb3Qgb2YgZGVmYXVsdHMgdGhhdCBhcmUgaW1wbGljaXQuDQpJdCBpcyB0aGVuIGZhc3QsIGJ1dCBzb21ldGltZXMgaXQgbWFrZXMgaXQgZGlmZmljdWx0IHRvIGtub3cgaWYgeW91IG1hZGUgeW91ciBwbG90IGNvcnJlY3RseS4gDQoNCmBgYHtyfQ0KcGxvdF9ub3RvdWxpZXJzJGRhdGENCg0KY29sbmFtZXMocGxvdF9ub3RvdWxpZXJzJGRhdGEpDQpgYGANCg0KDQpZb3UgY2FuIGFsc28gY29tcGFyZSBwbG90cyBvciBtYWtpbmcgY29tIHBvc2l0IHBsb3RzIHVzaW5nIHRoZSBsaWJyYXJ5IGFwbG90LiANCg0KDQpgYGB7cn0NCmxpYnJhcnkoYXBsb3QpDQpwbG90MSAvIHBsb3Rfbm90b3VsaWVycyANCmBgYA0KDQojIyAgRXhwb3J0aW5nIG91ciBkYXRhIChSIGZvcm1hdCkNCg0KIyMjIFJkcyBmb3JtYXQgDQotIFdlIGhhdmUgc2VlbiBwcmV2aW91c2x5IHRoYXQgd2UgY2FuIGV4cG9ydCBhIHRhYmxlIHRvIGEgY3N2IGZpbGUuIA0KDQpXZSBjYW4gYWxzbyBleHBvcnQgdGhlIGNvbnRlbnQgb2YgYSBSIE9iamVjdCB0byBhIGZpbGUsIHRoaXMgdG8gYmUgYWJsZSB0byByZS1pbXBvcnQgaXQgDQpsYXRlciBvbiBhbmQgZWcuIGNvbnRpbnVlIHdvcmtpbmcgb24gdGhhdCwgd2l0aG91dCBoYXZpbmcgdGhlIGhhc3NsZSBvZiBjaGVja2luZw0KdGhhdCBhbGwgdGhlIGRhdGEgdHlwZXMgYXJlIHJlYWQgYXMgd2Ugd2FudC4gDQoNCg0KYGBge3J9DQpzYXZlUkRTKGh1bWFuX2RhdGFfc2VsZWN0aW9uX2RlZHVwLCANCiAgICAgICAgaGVyZTo6aGVyZSgicmVzdWx0cyIsICJodW1hbl9kYXRhX3NlbGVjdGlvbl9kZWR1cC5yZHMiKSkNCmBgYA0KDQpUaGlzIGlzIHRoZSBjb21tYW5kIHRvIGFsbG93IHRvIHJlLWltcG9ydCB0aGUgZmlsZSBsYXRlciBvbiB0byBhbiBvYmplY3QNCmBgYHtyfQ0KbXlfdGFibGUgPC0gcmVhZFJEUyhoZXJlOjpoZXJlKCJyZXN1bHRzIiwgImh1bWFuX2RhdGFfc2VsZWN0aW9uX2RlZHVwLnJkcyIpKSANCnRhaWwobXlfdGFibGUpICMgc2hvd3MgdGhlIGxhc3Qgcm93cyBvZiB0aGUgdGFibGUNCmBgYA0KDQojIyMgRXhlcmNpY2UgOiBFeHBvcnRpbmcgYW5kIGltcG9ydGluZyBkYXRhIHRvIGFuZCBmcm9tIGEgY3N2IGZpbGUNCg0KLSB5b3UgY2FuIHRyeSB0byBmaW5kIGJhY2sgaG93IHRvIHJlYWQsIGV4cG9ydCB0aGUgc2FtZSBkYXRhIHRvIGEgY3N2IGZpbGUuDQoNCi0gaGludCAgOiBgP3dyaXRlLmNzdmAgYW5kIGA/cmVhZC5jc3ZgDQoNCg0KIyBTb21lIHVzZWZ1bGwgdHJpY2tzIGFuZCBub3Rlcw0KDQpBIGNvbXB1dGVyIGRvZXMgbm90IGxpa2UgdHlwb3MuIFRoZSBjb21tYW5kcyBoYXZlIHRvIGJlIHR5cGVkIGNvcnJlY3RseS4gDQpMZXR0ZXIgY2FzZXMsIHNwYWNlcyBhbmQgc3ltYm9scyBtYXR0ZXIuIE1vc3QgZXJyb3JzIG9mIGJlZ2lubmVycyBhcmUgZHVlIHRvIHR5cG9zLg0KSXQgdGFrZXMgYSBjZXJ0YWluIHRpbWUgdG8gYmUgYWJsZSB0byBzZWUgdGhlbSAod2VpcmQgYnV0IHRydWUpLiBTb21lIGhpZGRlbiBjaGFyYWN0ZXJzDQooZWcuIGVuZCBvZiBsaW5lcywgaW4gd2luZG93cyBjYXIgc29tZXRpbWVzIGJlIHByb2JsZW1hdGljIGFuZCBjYXVzZSBlcnJvcnMpDQoNCi0gYFRhYmAgaXMgeW91ciBmcmllZCB0byBhdm9pZCBhIGxvdCBvZiB3cml0aW5nLCBpdCBoZWxwcyB5b3UgYXV0by1jb21wbGV0ZSBhbmQgZ28gZmFzdGVyIGFuZCB3aXRob3V0IHNwZWxsaW5nIGVycm9ycyANCi0gYEVzY2FwZWAgd2hlbiB5b3UgaGFkIGRvbmUgdHlwaW5nIGVycm9yIGFuZCB0aGF0IHRoZSBjb25zb2xlIHN0YXJ0cyB3aXRoIGEgKyAodG8gYWJvcnQgeW91ciB0eXBpbmcpDQotIGBDdHJsICsgTGAgdG8gY2xlYXIgdGhlIGNvbnNvbGUNCi0gYEN0cmwgKyBFbnRlcmAgdG8gcnVuIHRoZSBjdXJyZW50IGxpbmUNCg0KLSBgQ3RybCArIEFsdCArIEkgYCB0byBpbnNlcnQgYSBuZXcgY29kZSBjaHVuayAobWlnaHQgYmUgZGlmZmVyZW50IGZvciB5b3UgLSB3cml0ZSB0aGUgc2hvcnRjdXQgaGVyZSAhICkNCi0gYEYxYCB0byBnZXQgdGhlIGhlbHAgb24gYSBmdW5jdGlvbiwgb2YgYD9mdW5jdGlvbmAuDQotIGBBcnJvd3MgdXAgYW5kIGRvd25gIHRvIHJlY2FsbCBvciBnbyBkb3duIHlvdXIgaGlzdG9yeSBvZiBjb21tYW5kcyBpbiB0aGUgY29uc29sZQ0KLSBgQ3RybCArIFNgIHRvIHNhdmUgdGhlIGZpbGUgY29udGFpbmluZyB5b3VyIGNvZGUgKCEgZGlmZmVyIHRvIGdpdCBodWIgImNvbW1pdCBhcyBzYXZlIHlvdXIgdmVyc2lvbiIgKQ0KDQoNCg0KLSBEbyBub3QgZm9yZ2V0IGFib3V0IHRoZSBwb3NzaWJpbGl0eSB0byB2aXN1YWxpemUgdGhlIGRhdGEgZnJhbWUgaW4gYW5vdGhlciB3aW5kb3csDQogIHNvIHlvdSBjYW4gbG9vayBhdCBpdCBhbmQgd3JpdGUgY29kZSBhdCB0aGUgc2FtZSB0aW1lLg0KLSBXaGVuIGV4cGVyaW1lbnRpbmcgd2l0aCBjb2RlOiBjb21tZW50aW5nIGFzc2lnbm1lbnQgYW5kIGNvbW1lbnRpbmcvdW4tY29tbWVudGluZyANCiAgcGlwZXMgb25lIHN0ZXAgYXQgdGhlIHRpbWUgYWxsb3cgeW91IHRvIGNoZWNrIHRoYXQgd2hhdCB5b3UgYXJlIGRvaW5nIGlzIGFzIGl0IHNob3VsZCBhbmQgcGlwZXMgY2ENCg0KLS0tDQoNCkEgcGFja2FnZSB0aGF0IEkgZmluZCBwcmFjdGljYWwgdG8gdXNlIChidXQgeW91IGNhbiBhbHNvIHNlYXJjaCBvbg0KdGhlIGludGVybmV0IGluc3RlYWQpIHdoZW4gSSBkbyBub3Qga25vdyB3aGF0IGZ1bmN0aW9ucyBhIHBhY2thZ2UNCmNvbnRhaW5zIGlzIHRoZSBwYWNrYWdlIHBhY21hbi4gSXQgYWxsb3dzIHlvdSB0byBsb29rIGF0IHdoaWNoIGZ1bmN0aW9ucw0KYXJlIGNvbnRhaW5lZCBpbiBhIHBhY2thZ2UuDQoNCmBgYHtyfQ0KbGlicmFyeShwYWNtYW4pDQpwX2Z1bmN0aW9ucyhkcGx5cikNCmBgYA0KDQpZb3UgY2FuIGFsc28gdXNlIHRoZSBlbWJlZGRlZCBoZWxwIGZ1bmN0aW9uIGluIFJzdHVkaW8gZm9yIHRoYXQ6DQoNCmBgYHRleHQNCj9kcGx5ciAjIGdpdmVzIHlvdSBpbmZvIG9uIHdoZXJlIHRvIGZpbmQgaW5mbw0KZHBseXI6OiAjIGFuZCB1c2UgdGhlIGFycm93cyB0byBleHBsb3JlIHRoZSBkaWZmZXJlbnQgZnVuY3Rpb25zIGNvbnRhaW5lZCBpbiB0aGUgcGFja2FnZQ0KYGBgDQoNCi0tLQ0KDQpUaGVyZSBhcmUgc2V2ZXJhbCBleGVyY2lzZXMgeW91IGNhbiBkbyBoZXJlIG9uIHlvdXIgb3duLiANClRyeSB0byB1bmRlcnN0YW5kIHdoYXQgaXQgZG9lcyBhbmQgdXNlIHRoZSBoZWxwLiANCkkgbGV0IHRob3NlIGV4ZXJjaXNlcyBiZWNhdXNlIHlvdSBzaG91bGQgYmUgYWJsZSB0byBtb2RpZnkgdGhlbSB0byB1c2Ugd2l0aCBvdGhlciB2YXJpYWJsZXMNCndoaWNoIGNhbiBoZWxwIHlvdSBzdGFydCAoYW5kIGF2b2lkIHNlYXJjaGluZyBhIGxvdCBvbiB0aGUgaW50ZXJuZXQpLiANCg0KPiBJIGhhZCBhY3R1YWxseSB0byBnb29nbGUgdG8gZmluZCBvdXQgaG93IHRvIGRvIHRoaW5ncy4gQW5kIGV2ZW4gaWYgSSBoYXZlIHNvbWUgZXhwZXJpZW5jZQ0Kd2l0aCB1c2luZyBSLCBJIHN0aWxsIGNvbnRyb2wgdGhhdCB0aGUgY29kZSBpcyBkb2luZyB3aGF0IGl0IGlzIHN1cHBvc2VkIHRvIGRvLiBFcnJvcg0KaXMgaHVtYW4gISANCg0KDQojIFdoZW4geW91IGFyZSByZWFkeSB0byBnbyBmdXJ0aGVyOg0KDQpZb3UgY2FuIHVzZSB0aG9zZSBsZXNzb25zIHRvIHJlcGVhdCB3aGF0IHlvdSBoYXZlIGxlYXJuZWQgKGJ1dCBpbg0KYW5vdGhlciB3YXkpIGFuZCBnbyBmdXJ0aGVyIGluIHlvdXIgbGVhcm5pbmcNCg0KLSAgIFtSIGZvciBSZXByb2R1Y2libGUgU2NpZW50aWZpYw0KICAgIEFuYWx5c2lzXShodHRwczovL3N3Y2FycGVudHJ5LmdpdGh1Yi5pby9yLW5vdmljZS1nYXBtaW5kZXIvKQ0KDQotICAgW1Byb2dyYW1taW5nIHdpdGgNCiAgICBSXShodHRwczovL3N3Y2FycGVudHJ5LmdpdGh1Yi5pby9yLW5vdmljZS1pbmZsYW1tYXRpb24vKQ0KDQpUaGUgW0NSQU5dKGh0dHBzOi8vY3Jhbi5yLXByb2plY3Qub3JnLykgd2Vic2l0ZSB5b3UgY2FuIGZpbmQgdGhlIHJlZmVyZW5jZSBtYW51YWxzDQpmb3IgUiBwcm9ncmFtbWluZyBsYW5ndWFnZS4gSWYgeW91IHJlYWxseSB3YW50IHRvIHVuZGVyc3RhbmQgdGhlIGJhc2ljcyBvZiB0aGUgbGFuZ3VhZ2UNCnlvdSBuZWVkIHRvIHN0YXJ0IHJlYWRpbmcgdGhlICJpbnRyb2R1Y3Rpb24gdG8gUiIgbWFudWFsLiANCg0KDQoNCg0KIyMgT3RoZXIgcmVzc291cmNlcyB0aGF0IEkgZWl0aGVyIHVzZWQgb3IgbG9vayBhdCBhbmQgZm91bmQgaWYgY291bGQgYmUgdXNlZnVsIGZvciB5b3Ugb25lIGRheSAuLi4gDQouLi4gIGFuZCBmb3IgbWUgdG8gcmVtaW5kIHdoZXJlIEkgZm91bmQgdGhpcyBpbmZvcm1hdGlvbg0KDQotIFtSYmxvZ2dlciBleGFtcGxlc10oaHR0cHM6Ly93d3cuci1ibG9nZ2Vycy5jb20vMjAyMi8wOS9yLW1hcmtkb3duLWxlc3Nlci1rbm93bi10aXBzLXRyaWNrcy00LWxvb2tzLWJldHRlci13b3Jrcy1iZXR0ZXIvKQ0KLSBbRm9sZHMgaW4gUm1hcmtkb3duXShodHRwczovL2Jvb2tkb3duLm9yZy95aWh1aS9ybWFya2Rvd24tY29va2Jvb2svZm9sZC1zaG93Lmh0bWwpDQotIFtTdHlsaW5nXShodHRwczovL3JwdWJzLmNvbS9jb2hlbnNoLzgzMzU1MCkNCi0gW1doaXRlc3BhY2VzIGh0bWwgJmVuc3A7XShodHRwczovL3N0YWNrb3ZlcmZsb3cuY29tL3F1ZXN0aW9ucy80NDgxMDUxMS9ob3ctdG8tYWRkLWVtcHR5LXNwYWNlcy1pbnRvLW1kLW1hcmtkb3duLXJlYWRtZS1vbi1naXRodWIpDQotIFthbHRlcm5hdGl2ZSBodG1sIHRhYmxlczogcGFuZHJdKGh0dHBzOi8vY3Jhbi5yLXByb2plY3Qub3JnL3dlYi9wYWNrYWdlcy9wYW5kZXIvdmlnbmV0dGVzL2tuaXRyLmh0bWwpDQotIFtleGVjdXRhYmxlIGVudmlyb25tZW50c10oaHR0cHM6Ly9teWJpbmRlci5vcmcvKQ0KLSBbcmVwcm9kdWNpYmxlIHJlc2VhcmNoXShpbnRybyBodHRwczovL2RhdGFjYXJwZW50cnkub3JnL3JyLWludHJvL2Fpby5odG1sKQ0KLSBbSHVtb3IgY29kZSBjb250ZW50XShodHRwczovL21pcm8ubWVkaXVtLmNvbS92Mi9yZXNpemU6Zml0OjE0MDAvMCpfbGJma0gyUmRXdlF5TFNRLnBuZykNCg0KQmFjayB0byBbSW5kZXhdKGluZGV4Lmh0bWwp