Data Cleaning is a topic that is often requested at the SCC and has begged the question, “Why is it not covered in our intro research courses?” Though it would be intuitive to integrate into the stats sequence, the actual application is often difficult to implement (In other words, easier said than done).

The reason for this is because data cleaning isn’t simply an operation, but a process; a process that varies widely based on your specific data structure and what you’re trying to accomplish. It’s further complicated that data cleaning often takes multiple steps to get your data ready to implement analysis.

Given all of these challenges, the goal of this page is to provide a sample of common data cleaning steps using quasi-real-world data (i.e. I made it up, but it looks pretty legit) that you may use to help guide your data cleaning adventures. As always, if you have any questions or would like to see more similar content, feel free to reach out to the SCC via email or Request an appointment. So without further ado, let’s start coding.

SPSS Integration

For the majority of people reading this, your data will be in SPSS format. SPSS is a great package for non-data enthusiasts, however it’s data cleaning abilities are very sparse. Additionally, the data cleaning you can do in SPSS, takes much more time and energy than through RStudio.

Fortunately, reading SPSS data in R is very easy! With the few lines below, you should be able to read your data directly into R, clean it, and export it into SPSS format, ready for you to work your statistical magic in the system most of you have come to know and love!

Before we get started, you’ll want to set your working directory. This will tell R where to import files from and export files to. Check this image out on how to set your working direcory:

Once you select the folder you want to use, you should be all set!

Next you’re gonna want to install and load the haven package.

install.packages("haven")
library(haven)

Now that we have that ready to go, we’re gonna want to read in our data using the read_sav() function. But remember, we have to create an object that we can call back while cleaning. To do this is simple, simply give your data a name, like data and use the <- to denote an object.

data <- read_sav(file = "data.sav")

Congratulations!! Your data is now in R, and we can start cleaning! Before we get started, let’s take a look at the first 6 lines to make sure we have the right dataset:

head(data)

Recoding Variables

One of the most used, and probably the easiest, cleaning operation is recoding variables. Whether we want to dummy code or group values together, R can get you where you need to go with a few lines of code.

Dummy Code

Let’s take a look at our Gender variable in column 3. When running any kind of regression, we are gonna want to recode this variable where we have 1 variable as a reference group and another as our “group of interest”. First things first, let’s take a closer look at what categories we have an how many participants in each category

table(data$Gender)

   F    M   NB 
1430  738  240 

Our data shows we have 3 categories: Female = 1430 participants, Male = 738 participants, and Non-binary = 240 participants. In order to effectively dummy code, we want to select a reference group that we will compare the other groups to during our regression analysis. I will arbitrarily select the Female group as our reference group. This means we need to create dummy-coded variables for the Male group and the Non-binary group.

data$male_d <- ifelse(data$Gender=="M",1,0)
data$NB_d <- ifelse(data$Gender=="NB",1,0)

There’s a lot happening in this small chunk of code, so I’ll try to break it down as best as I can. The first thing you’ll notice is the $ in the chunk reading data$male_d. For our purposes, this little symbol specifies a variable within a dataframe. When it’s on the left side of a <-, it is creating a new, or updating an existing, variable in the dataset. If it’s on the right side of the <- it is typically referencing a variable in an equation or function.

The next thing you’ll notice is we used the ifelse function. This is a very common and effective function that reads ifelse(test,if TRUE, if FALSE). Broken down a little further, the test is a logical operation where we say if the statement “data$Gender equals (==) M” is TRUE. The next vales if TRUE and if FALSE simply say what we want those respective values to be after this is run.

To put it all together we’re saying, “create a new variable, male_d in dataframe data, where if data$Gender says”M", recode that into 1, for anything else, recode those into 0.

Recoding variable into distinct groups

Let’s take a look at a situation where you have a continuous value that you want to group into a few distinct categories. We’ll shift our attention to a variable at the very end OQ.

First things first, lets get an idea of what we’re working with:

summary(data$OQ)
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
   0.00   29.00   34.00   39.39   47.00  105.00 

We’re gonna group this variable into 4 categories using the case_when function from the dplyr package. This package is part of the tidyverse family (that’s a whole other topic). For now lets get into some code.

library(tidyverse)
Registered S3 methods overwritten by 'dbplyr':
  method         from
  print.tbl_lazy     
  print.tbl_sql      
-- Attaching packages ---------------------------------- tidyverse 1.3.1 --
v ggplot2 3.3.5     v purrr   0.3.4
v tibble  3.1.3     v dplyr   1.0.7
v tidyr   1.1.3     v stringr 1.4.0
v readr   2.0.1     v forcats 0.5.1
-- Conflicts ------------------------------------- tidyverse_conflicts() --
x dplyr::filter() masks stats::filter()
x dplyr::lag()    masks stats::lag()

We’re gonna divide the 4 categories using the 4 quartiles as listed above.

data$OQ_cat<- case_when(
  data$OQ<=29 ~"1",
  data$OQ>=30&data$OQ<=34 ~"2",
  data$OQ>=35&data$OQ<=47 ~"3",
  data$OQ>=48 ~"4"
)

data$OQ_cat<-as.numeric(data$OQ_cat)

Now is probably a good time to talk about logical operators. In the above code you see a lot of random symbols that are probably scaring a lot of people off. But if you just bear with me for a few more sentences, I’ll explain everything. We should all be familiar with some of these, but they could still be a little cryptic:

> greater than
>= greater than or equal to
< less than
<= less than or equal to
== is equal to
!= is not equal to

& and
| or

With our newfound knowledge, let’s take a closer look at how we created category 2. data$OQ>=30&data$OQ<=34 ~ "2"
If we take is slow, we can see we specified “if OQ score is greater than or equal to 30(1 above 1st quartile) AND OQ score is less than or equal to 34 (median), then recode into 2”

Then the last line I’m just changing the variable type from character to numeric. If a variable is stored as numeric and we wanted to convert it to character, we could call as.character()

Subsetting Datasets

I’m not actually sure if you could do this in SPSS or not without jumping through multiple hoops. There’s many ways you might want to subset a dataset, so I’ll show the most common.

Keeping specific variables

A lot of times you’ll acquire a gargantuan of a dataset and you only need a few variables. I personally hate having a bunch of vars in my dataset that I’m not even gonna use. We’ll use the subset() function to trim our original dataset data into something more manageable. As always, let’s first get a good look at our current dataset:

pillar::glimpse(data)
Rows: 2,408
Columns: 37
$ Client_ID <chr> "5054", "5055", "2101", "2102", "2103", "2104", "2105"~
$ DOB       <chr> "5/19/1987", "5/21/1970", "3/18/1974", "8/30/1983", "7~
$ Gender    <chr> "F", "F", "M", "F", "M", "NB", "NB", "F", "F", "F", "F~
$ Ethnicity <chr> "African American", "Asian", "Hispanic", "Asian", "Whi~
$ tst1      <dbl> 2, 2, 2, 4, 2, 2, 2, 3, 2, 2, 3, 3, 2, 3, 1, 4, 3, 2, ~
$ tst2      <dbl> 3, 3, 3, 1, 4, 2, 2, 3, 2, 3, 3, 4, 3, 3, 2, 3, 3, 2, ~
$ tst3      <dbl> 3, 4, 2, 4, 2, 1, 2, 3, 1, 2, 1, 3, 1, 1, 3, 2, 2, 3, ~
$ tst4      <dbl> 2, 3, 3, 3, 2, 1, 3, 1, 1, 2, 2, 1, 3, 4, 3, 1, 3, 3, ~
$ tst5      <dbl> 2, 3, 3, 2, 4, 2, 3, 1, 4, 3, 2, 3, 3, 1, 1, 3, 1, 2, ~
$ tst6      <dbl> 3, 3, 4, 3, 2, 3, 3, 2, 3, 2, 2, 3, 3, 3, 2, 3, 3, 3, ~
$ tst7      <dbl> 1, 3, 3, 3, 4, 3, 2, 2, 3, 1, 3, 1, 2, 3, 3, 2, 1, 3, ~
$ tst8      <dbl> 1, 3, 3, 2, 2, 4, 3, 3, 1, 2, 3, 2, 4, 1, 3, 3, 2, 2, ~
$ tst9      <dbl> 3, 4, 4, 3, 3, 3, 3, 1, 3, 4, 3, 2, 3, 4, 3, 2, 4, 3, ~
$ tst10     <dbl> 2, 3, 4, 3, 2, 2, 3, 3, 1, 3, 2, 3, 2, 3, 2, 3, 2, 2, ~
$ AB1       <dbl> 31.34906, 31.06828, 27.65837, 27.18045, 43.28128, 27.6~
$ AB2       <dbl> 39.74240, 29.36426, 38.12954, 31.67078, 31.11556, 20.2~
$ AB3       <dbl> 30.14401, 34.13057, 29.19953, 33.83375, 25.05884, 37.7~
$ AB4       <dbl> 26.72113, 28.84908, 28.33457, 30.57840, 23.98519, 45.6~
$ AB5       <dbl> 27.08153, 30.94320, 28.46713, 27.94193, 28.94680, 36.5~
$ AB6       <dbl> 37.77197, 40.14996, 35.14671, 35.61309, 26.40682, 26.6~
$ AB7       <dbl> 30.65428, 32.48099, 36.51662, 26.46737, 39.82626, 22.4~
$ AB8       <dbl> 34.18883, 31.73529, 31.76150, 20.06505, 31.64857, 36.9~
$ AB9       <dbl> 16.19063, 27.65630, 30.66528, 40.31240, 35.77346, 37.9~
$ AB10      <dbl> 26.41643, 35.01845, 20.57879, 29.70228, 25.66419, 21.0~
$ AB11      <dbl> 34.15993, 28.87851, 17.84969, 31.60336, 20.46157, 27.0~
$ AB12      <dbl> 26.84247, 34.85961, 26.03678, 35.39373, 25.32589, 35.2~
$ AB13      <dbl> 19.04151, 28.30442, 33.32491, 29.72498, 36.34334, 35.5~
$ AB14      <dbl> 31.62268, 25.56272, 34.20726, 25.37035, 23.07195, 30.8~
$ AB15      <dbl> 23.38902, 31.24178, 37.98436, 28.39059, 22.85218, 26.7~
$ AB16      <dbl> 27.53183, 31.25811, 42.46906, 36.16505, 28.68694, 29.7~
$ AB17      <dbl> 28.75115, 33.00335, 26.43749, 27.02463, 30.38442, 26.4~
$ AB18      <dbl> 29.82952, 28.52266, 36.79014, 29.77278, 29.85245, 32.4~
$ AB19      <dbl> 19.53882, 30.67495, 35.59165, 29.36187, 29.76933, 35.7~
$ AB20      <dbl> 35.81505, 25.74468, 24.07690, 22.37588, 27.02414, 33.8~
$ OQ        <dbl> 32, 32, 22, 47, 23, 33, 31, 22, 22, 28, 22, 22, 22, 31~
$ SI        <chr> "N", "N", "N", "N", "Y", "N", "N", "Y", "N", "N", "N",~
$ OQ_cat    <dbl> 2, 2, 1, 3, 1, 2, 2, 1, 1, 1, 1, 1, 1, 2, 3, 1, 1, 1, ~

Now that we know what we’re working with, let’s use the subset() function to keep only relevant variables:

newdata<- subset(data, select = c(Client_ID,DOB,Gender,Ethnicity,OQ,tst1:tst10))

pillar::glimpse(newdata)
Rows: 2,408
Columns: 15
$ Client_ID <chr> "5054", "5055", "2101", "2102", "2103", "2104", "2105"~
$ DOB       <chr> "5/19/1987", "5/21/1970", "3/18/1974", "8/30/1983", "7~
$ Gender    <chr> "F", "F", "M", "F", "M", "NB", "NB", "F", "F", "F", "F~
$ Ethnicity <chr> "African American", "Asian", "Hispanic", "Asian", "Whi~
$ OQ        <dbl> 32, 32, 22, 47, 23, 33, 31, 22, 22, 28, 22, 22, 22, 31~
$ tst1      <dbl> 2, 2, 2, 4, 2, 2, 2, 3, 2, 2, 3, 3, 2, 3, 1, 4, 3, 2, ~
$ tst2      <dbl> 3, 3, 3, 1, 4, 2, 2, 3, 2, 3, 3, 4, 3, 3, 2, 3, 3, 2, ~
$ tst3      <dbl> 3, 4, 2, 4, 2, 1, 2, 3, 1, 2, 1, 3, 1, 1, 3, 2, 2, 3, ~
$ tst4      <dbl> 2, 3, 3, 3, 2, 1, 3, 1, 1, 2, 2, 1, 3, 4, 3, 1, 3, 3, ~
$ tst5      <dbl> 2, 3, 3, 2, 4, 2, 3, 1, 4, 3, 2, 3, 3, 1, 1, 3, 1, 2, ~
$ tst6      <dbl> 3, 3, 4, 3, 2, 3, 3, 2, 3, 2, 2, 3, 3, 3, 2, 3, 3, 3, ~
$ tst7      <dbl> 1, 3, 3, 3, 4, 3, 2, 2, 3, 1, 3, 1, 2, 3, 3, 2, 1, 3, ~
$ tst8      <dbl> 1, 3, 3, 2, 2, 4, 3, 3, 1, 2, 3, 2, 4, 1, 3, 3, 2, 2, ~
$ tst9      <dbl> 3, 4, 4, 3, 3, 3, 3, 1, 3, 4, 3, 2, 3, 4, 3, 2, 4, 3, ~
$ tst10     <dbl> 2, 3, 4, 3, 2, 2, 3, 3, 1, 3, 2, 3, 2, 3, 2, 3, 2, 2, ~

The subset() function uses 2 primary arguments, the first is the original dataset; in our case, it was data. The next argument is the criteria for subsetting. In this first case, it was selecting specific variables.

Let’s say we only want to get rid of a couple variables. It would be really time consuming (and mistake prone) to write out 20 different variables that we wanted to keep. We would instead add a - in front of c() and enter the variables we would like to exclude. It would look something like this:
newdata<-subset(data, select = -c(SI, OQ_cat))

Keeping/discarding rows

If we want to keep participants based on their level on a given variable, we can also turn to the subset() function:

### Keeping only female participants
female_ds <- subset(data, data$Gender=="F")

#Notice the difference in the Gender counts between data & female_ds
table(data$Gender)

   F    M   NB 
1430  738  240 
table(female_ds$Gender)

   F 
1430 
## Keeping participants with scores between 20-30 on OQ
OQ_dat <- subset(data, data$OQ>=20 & data$OQ<=30)

# Notice the min. & max. for both
summary(data$OQ)
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
   0.00   29.00   34.00   39.39   47.00  105.00 
summary(OQ_dat$OQ)
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
  20.00   23.00   27.00   25.94   28.00   30.00 

Keeping specific rows & randomly sampling participants

This one’s pretty common when you want to keep a holdout validation set (if anyone’s into machine learning).

Let’s say we want to use just the first 500 rows of data. First let’s look at how many rows data has:

nrow(data)
[1] 2408

Next, lets subset using these handy-dandy []:

first500<- data[1:500,]
nrow(first500)
[1] 500

The brackets are predoninantly used in R (specifically when working with dataframes) to reference rows and columns in the dataset. The argument in front of the comma (1:500) is in reference to the rows. Here we wanted rows 1 through 500. We also wanted to keep all the columns, so we didn’t specify anything after the comma. say we wanted to keep the first 500 rows and the first 10 columns. Our code would look something like this:

first500<-data[1:500,1:10]

Split-half Random Sample

Instead of just taking the first 500 or so rows, we can randomly sample our dataset like so:

set.seed(1234)
splt<-sample(nrow(data),nrow(data)*.5)

half_dat<-data[splt,]

nrow(half_dat)

I know, I know, I have some explaining to do…

First off, set.seed(1234) is used when you’re doing something random in R (i.e. random sampling, running a machine learning algo, even using maximum likelihood or Bayesian) and you want to replicate your results.

The next line is simply randomly generating a sample of numbers between 1 and the number of rows in data (2408). This is what the first argument nrow(data) means. The second argument nrow(data)*.5, means make the sample half the number of rows of data, data rows * .5

Now that we have a random sample of digits that is equal to half of data, we are gonna make those digits represent the exact rows we want to keep from data. Remember the brackets []? We used the same principle from above where we kept rows 1:500, except we used our random sample we created, and specified we wanted to keep all the columns.

Next thing we would want to do is recover the other half of the dataset that isn’t represented in half_dat. To do this, you simply:

other_half<- data[-splt,]

Now you have a completely complimentary dataset.

Random Tidbits

As I wind down this article I’ll give a few random tidbits to help you clean your data.

Working with dates

We have a variable DOB that represents Date of Birth. Unfortunately, we don’t have age, but we can calculate it with relative ease.

First, let’s take a look at what kind of variable R thinks it is

class(data$DOB)
[1] "character"

As I suspected R doesn’t think this is a date, which makes it tough to calculate age. We’re gonna have to change the type before we do anything.

R’s date/time repritoire is not quite as sophistocated as SAS, but it can do the job. R’s base package has the function as.Date() that helps us out. Let’s take a look at how we can convert the date in our dataset to a format that R can recognize:

data$DOB<- as.Date(data$DOB, format = "%m/%d/%Y")

class(data$DOB)
[1] "Date"

The format = argument allows you to specify how your date is currently formatted. Since ours was mm/dd/yyyy, we used “%m/%d/%Y”. If our format was “mm-dd-yy” for example, it would read “%m-%d-%y”.

Cool! Now we can calculate age. We’ll use the lubridate package to help us out

data$age<- lubridate::interval(data$DOB,Sys.Date())%/%months(12)

summary(data$age)
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
   8.00   34.00   41.00   40.77   48.00   81.00 

The package lubridate has many useful functions that I won’t get into here, but just know they’re there. If you ever want to explore a package or function any further, just run the command help(lubridate).
The interval function allows you to calculate an interval between 2 timepoints. The %/% is what we call a “pipe operator” and is a staple in the tidyverse. It provides a simple way to perform a mathematical operation on the results of a function with just 1 line of code. Here what we’re telling it to do is calculate the interval between each participant’s DOB and divide that interval by 12 months, which will give us the age of each pariticpant. From here we can enter it into our equation like we would any normal variable.

Substring

Substring may come in handy if you want to take a snippet of a variable. To be honest, I primarily use this when I get a date/time variable and I just want to keep the date. To illustrate this point, I’m going to make a variable in data that has the current date/time and subset from there.

data$current <- rep(Sys.time(), nrow(data))

data$current[1]
[1] "2021-12-07 09:39:04 PST"

Now that we’re here, I might as well talk about the rep command. This stands for “repeat” and is a good way to create repetitions in R. Right now, I’m saying make a new variable called current and fill it with a series of the current date and time, for the number of rows in data.

Back to substring. We’re gonna use the command substr() to keep only the date (2021-12-07). The arguments read: 1) what do we want to subset 2) at which character do we start 3) at which character do we stop. Since the date starts at 1 and ends at 10, we’ll go ahead and put those arguments in.

data$current_date<- substr(data$current,1,10)

data$current_date[1]
[1] "2021-12-07"

SPSS Reintegration

Last thing we need to do is rewrite your data back into SPSS format. This is a 1 line command that’ll allow you to take your cleaned data from R and read it back in to SPSS.

haven::write_sav(newdata, "newdata.sav")

You should see a file titled “newdata” in the folder you’ve been working out of. If not, make sure to go back and set your working directory back to the folder of your choice.

Conclusion

That’s gonna do it for this page. Remember, data cleaning is a process so you’ll most likely have to do a few of these steps or slight variations on my provided code. Last bit of advice, Google has been my best friend since I’ve started learning R. You can find a solution to almost any error with a quick search. And as always, if you are still having trouble or want me to cover anything esle, feel free to shoot me an email at scc@paloaltou.edu

By: Joe Razo SCC Student Director December, 2021

LS0tDQp0aXRsZTogIkRhdGEgQ2xlYW5pbmciDQpBdXRob3I6ICJKb2UgUmF6byINCm91dHB1dDogaHRtbF9ub3RlYm9vaw0KLS0tDQoNCkRhdGEgQ2xlYW5pbmcgaXMgYSB0b3BpYyB0aGF0IGlzIG9mdGVuIHJlcXVlc3RlZCBhdCB0aGUgU0NDIGFuZCBoYXMgYmVnZ2VkIHRoZSBxdWVzdGlvbiwgIldoeSBpcyBpdCBub3QgY292ZXJlZCBpbiBvdXIgaW50cm8gcmVzZWFyY2ggY291cnNlcz8iIFRob3VnaCBpdCB3b3VsZCBiZSBpbnR1aXRpdmUgdG8gaW50ZWdyYXRlIGludG8gdGhlIHN0YXRzIHNlcXVlbmNlLCB0aGUgYWN0dWFsIGFwcGxpY2F0aW9uIGlzIG9mdGVuIGRpZmZpY3VsdCB0byBpbXBsZW1lbnQgKEluIG90aGVyIHdvcmRzLCBlYXNpZXIgc2FpZCB0aGFuIGRvbmUpLiANCg0KVGhlIHJlYXNvbiBmb3IgdGhpcyBpcyBiZWNhdXNlIGRhdGEgY2xlYW5pbmcgaXNuJ3Qgc2ltcGx5IGFuIG9wZXJhdGlvbiwgYnV0IGEgcHJvY2VzczsgYSBwcm9jZXNzIHRoYXQgdmFyaWVzIHdpZGVseSBiYXNlZCBvbiB5b3VyIHNwZWNpZmljIGRhdGEgc3RydWN0dXJlIGFuZCB3aGF0IHlvdSdyZSB0cnlpbmcgdG8gYWNjb21wbGlzaC4gSXQncyBmdXJ0aGVyIGNvbXBsaWNhdGVkIHRoYXQgZGF0YSBjbGVhbmluZyBvZnRlbiB0YWtlcyBtdWx0aXBsZSBzdGVwcyB0byBnZXQgeW91ciBkYXRhIHJlYWR5IHRvIGltcGxlbWVudCBhbmFseXNpcy4gDQoNCkdpdmVuIGFsbCBvZiB0aGVzZSBjaGFsbGVuZ2VzLCB0aGUgZ29hbCBvZiB0aGlzIHBhZ2UgaXMgdG8gcHJvdmlkZSBhIHNhbXBsZSBvZiBjb21tb24gZGF0YSBjbGVhbmluZyBzdGVwcyB1c2luZyBxdWFzaS1yZWFsLXdvcmxkIGRhdGEgKGkuZS4gSSBtYWRlIGl0IHVwLCBidXQgaXQgbG9va3MgcHJldHR5IGxlZ2l0KSB0aGF0IHlvdSBtYXkgdXNlIHRvIGhlbHAgZ3VpZGUgeW91ciBkYXRhIGNsZWFuaW5nIGFkdmVudHVyZXMuIEFzIGFsd2F5cywgaWYgeW91IGhhdmUgYW55IHF1ZXN0aW9ucyBvciB3b3VsZCBsaWtlIHRvIHNlZSBtb3JlIHNpbWlsYXIgY29udGVudCwgZmVlbCBmcmVlIHRvIHJlYWNoIG91dCB0byB0aGUgU0NDIHZpYSBbZW1haWxdKG1haWx0bzpzY2NAcGFsb2FsdG91LmVkdSkgb3IgW1JlcXVlc3QgYW4gYXBwb2ludG1lbnRdKGh0dHA6Ly9zY2MucGFsb2FsdG91LmVkdS8pLiBTbyB3aXRob3V0IGZ1cnRoZXIgYWRvLCBsZXQncyBzdGFydCBjb2RpbmcuIA0KDQojIyBTUFNTIEludGVncmF0aW9uDQpGb3IgdGhlIG1ham9yaXR5IG9mIHBlb3BsZSByZWFkaW5nIHRoaXMsIHlvdXIgZGF0YSB3aWxsIGJlIGluIFNQU1MgZm9ybWF0LiBTUFNTIGlzIGEgZ3JlYXQgcGFja2FnZSBmb3Igbm9uLWRhdGEgZW50aHVzaWFzdHMsIGhvd2V2ZXIgaXQncyBkYXRhIGNsZWFuaW5nIGFiaWxpdGllcyBhcmUgdmVyeSBzcGFyc2UuIEFkZGl0aW9uYWxseSwgdGhlIGRhdGEgY2xlYW5pbmcgeW91IGNhbiBkbyBpbiBTUFNTLCB0YWtlcyBtdWNoIG1vcmUgdGltZSBhbmQgZW5lcmd5IHRoYW4gdGhyb3VnaCBSU3R1ZGlvLiANCg0KRm9ydHVuYXRlbHksIHJlYWRpbmcgU1BTUyBkYXRhIGluIFIgaXMgdmVyeSBlYXN5ISBXaXRoIHRoZSBmZXcgbGluZXMgYmVsb3csIHlvdSBzaG91bGQgYmUgYWJsZSB0byByZWFkIHlvdXIgZGF0YSBkaXJlY3RseSBpbnRvIFIsIGNsZWFuIGl0LCBhbmQgZXhwb3J0IGl0IGludG8gU1BTUyBmb3JtYXQsIHJlYWR5IGZvciB5b3UgdG8gd29yayB5b3VyIHN0YXRpc3RpY2FsIG1hZ2ljIGluIHRoZSBzeXN0ZW0gbW9zdCBvZiB5b3UgaGF2ZSBjb21lIHRvIGtub3cgYW5kIGxvdmUhIA0KDQpCZWZvcmUgd2UgZ2V0IHN0YXJ0ZWQsIHlvdSdsbCB3YW50IHRvIHNldCB5b3VyIHdvcmtpbmcgZGlyZWN0b3J5LiBUaGlzIHdpbGwgdGVsbCBSIHdoZXJlIHRvIGltcG9ydCBmaWxlcyBmcm9tIGFuZCBleHBvcnQgZmlsZXMgdG8uIENoZWNrIHRoaXMgaW1hZ2Ugb3V0IG9uIGhvdyB0byBzZXQgeW91ciB3b3JraW5nIGRpcmVjb3J5Og0KDQohW10oQzpcVXNlcnNcam9zZXBcRGVza3RvcFxHcmFkIFNjaG9vbFxTQ0NcU0NDIFN0dWZmXHNldHdkLnBuZykgDQoNCk9uY2UgeW91IHNlbGVjdCB0aGUgZm9sZGVyIHlvdSB3YW50IHRvIHVzZSwgeW91IHNob3VsZCBiZSBhbGwgc2V0ISAgDQoNCk5leHQgeW91J3JlIGdvbm5hIHdhbnQgdG8gaW5zdGFsbCBhbmQgbG9hZCB0aGUgYGBgaGF2ZW5gYGAgcGFja2FnZS4NCg0KYGBge3J9DQppbnN0YWxsLnBhY2thZ2VzKCJoYXZlbiIpDQpsaWJyYXJ5KGhhdmVuKQ0KYGBgDQoNCk5vdyB0aGF0IHdlIGhhdmUgdGhhdCByZWFkeSB0byBnbywgd2UncmUgZ29ubmEgd2FudCB0byByZWFkIGluIG91ciBkYXRhIHVzaW5nIHRoZSBgYGByZWFkX3NhdigpYGBgIGZ1bmN0aW9uLiBCdXQgcmVtZW1iZXIsIHdlIGhhdmUgdG8gY3JlYXRlIGFuIG9iamVjdCB0aGF0IHdlIGNhbiBjYWxsIGJhY2sgd2hpbGUgY2xlYW5pbmcuIFRvIGRvIHRoaXMgaXMgc2ltcGxlLCBzaW1wbHkgZ2l2ZSB5b3VyIGRhdGEgYSBuYW1lLCBsaWtlIGBgYGRhdGFgYGAgYW5kIHVzZSB0aGUgYGBgPC1gYGAgdG8gZGVub3RlIGFuIG9iamVjdC4NCg0KYGBge3J9DQpkYXRhIDwtIHJlYWRfc2F2KGZpbGUgPSAiZGF0YS5zYXYiKQ0KYGBgDQoNCkNvbmdyYXR1bGF0aW9ucyEhIFlvdXIgZGF0YSBpcyBub3cgaW4gUiwgYW5kIHdlIGNhbiBzdGFydCBjbGVhbmluZyEgQmVmb3JlIHdlIGdldCBzdGFydGVkLCBsZXQncyB0YWtlIGEgbG9vayBhdCB0aGUgZmlyc3QgNiBsaW5lcyB0byBtYWtlIHN1cmUgd2UgaGF2ZSB0aGUgcmlnaHQgZGF0YXNldDoNCg0KYGBge3J9DQpoZWFkKGRhdGEpDQpgYGANCg0KIyMgUmVjb2RpbmcgVmFyaWFibGVzDQoNCk9uZSBvZiB0aGUgbW9zdCB1c2VkLCBhbmQgcHJvYmFibHkgdGhlIGVhc2llc3QsIGNsZWFuaW5nIG9wZXJhdGlvbiBpcyByZWNvZGluZyB2YXJpYWJsZXMuIFdoZXRoZXIgd2Ugd2FudCB0byBkdW1teSBjb2RlIG9yIGdyb3VwIHZhbHVlcyB0b2dldGhlciwgUiBjYW4gZ2V0IHlvdSB3aGVyZSB5b3UgbmVlZCB0byBnbyB3aXRoIGEgZmV3IGxpbmVzIG9mIGNvZGUuIA0KDQojIyMgRHVtbXkgQ29kZQ0KDQpMZXQncyB0YWtlIGEgbG9vayBhdCBvdXIgYGBgR2VuZGVyYGBgIHZhcmlhYmxlIGluIGNvbHVtbiAzLiBXaGVuIHJ1bm5pbmcgYW55IGtpbmQgb2YgcmVncmVzc2lvbiwgd2UgYXJlIGdvbm5hIHdhbnQgdG8gcmVjb2RlIHRoaXMgdmFyaWFibGUgd2hlcmUgd2UgaGF2ZSAxIHZhcmlhYmxlIGFzIGEgcmVmZXJlbmNlIGdyb3VwIGFuZCBhbm90aGVyIGFzIG91ciAiZ3JvdXAgb2YgaW50ZXJlc3QiLiBGaXJzdCB0aGluZ3MgZmlyc3QsIGxldCdzIHRha2UgYSBjbG9zZXIgbG9vayBhdCB3aGF0IGNhdGVnb3JpZXMgd2UgaGF2ZSBhbiBob3cgbWFueSBwYXJ0aWNpcGFudHMgaW4gZWFjaCBjYXRlZ29yeQ0KDQpgYGB7cn0NCnRhYmxlKGRhdGEkR2VuZGVyKQ0KYGBgDQoNCk91ciBkYXRhIHNob3dzIHdlIGhhdmUgMyBjYXRlZ29yaWVzOiBgYGBGZW1hbGVgYGAgPSAxNDMwIHBhcnRpY2lwYW50cywgYGBgTWFsZWBgYCA9IDczOCBwYXJ0aWNpcGFudHMsIGFuZCBgYGBOb24tYmluYXJ5YGBgID0gMjQwIHBhcnRpY2lwYW50cy4gDQpJbiBvcmRlciB0byBlZmZlY3RpdmVseSBkdW1teSBjb2RlLCB3ZSB3YW50IHRvIHNlbGVjdCBhIHJlZmVyZW5jZSBncm91cCB0aGF0IHdlIHdpbGwgY29tcGFyZSB0aGUgb3RoZXIgZ3JvdXBzIHRvIGR1cmluZyBvdXIgcmVncmVzc2lvbiBhbmFseXNpcy4gSSB3aWxsIGFyYml0cmFyaWx5IHNlbGVjdCB0aGUgYGBgRmVtYWxlYGBgIGdyb3VwIGFzIG91ciByZWZlcmVuY2UgZ3JvdXAuIFRoaXMgbWVhbnMgd2UgbmVlZCB0byBjcmVhdGUgZHVtbXktY29kZWQgdmFyaWFibGVzIGZvciB0aGUgYGBgTWFsZWBgYCBncm91cCBhbmQgdGhlIGBgYE5vbi1iaW5hcnlgYGAgZ3JvdXAuIA0KYGBge3J9DQpkYXRhJG1hbGVfZCA8LSBpZmVsc2UoZGF0YSRHZW5kZXI9PSJNIiwxLDApDQpkYXRhJE5CX2QgPC0gaWZlbHNlKGRhdGEkR2VuZGVyPT0iTkIiLDEsMCkNCmBgYA0KDQpUaGVyZSdzIGEgbG90IGhhcHBlbmluZyBpbiB0aGlzIHNtYWxsIGNodW5rIG9mIGNvZGUsIHNvIEknbGwgdHJ5IHRvIGJyZWFrIGl0IGRvd24gYXMgYmVzdCBhcyBJIGNhbi4gVGhlIGZpcnN0IHRoaW5nIHlvdSdsbCBub3RpY2UgaXMgdGhlIGBgYCRgYGAgaW4gdGhlIGNodW5rIHJlYWRpbmcgYGBgZGF0YSRtYWxlX2RgYGAuIEZvciBvdXIgcHVycG9zZXMsIHRoaXMgbGl0dGxlIHN5bWJvbCBzcGVjaWZpZXMgYSB2YXJpYWJsZSB3aXRoaW4gYSBkYXRhZnJhbWUuIFdoZW4gaXQncyBvbiB0aGUgbGVmdCBzaWRlIG9mIGEgYGBgPC1gYGAsIGl0IGlzIGNyZWF0aW5nIGEgbmV3LCBvciB1cGRhdGluZyBhbiBleGlzdGluZywgdmFyaWFibGUgaW4gdGhlIGRhdGFzZXQuIElmIGl0J3Mgb24gdGhlIHJpZ2h0IHNpZGUgb2YgdGhlIGBgYDwtYGBgIGl0IGlzIHR5cGljYWxseSByZWZlcmVuY2luZyBhIHZhcmlhYmxlIGluIGFuIGVxdWF0aW9uIG9yIGZ1bmN0aW9uLg0KDQoNClRoZSBuZXh0IHRoaW5nIHlvdSdsbCBub3RpY2UgaXMgd2UgdXNlZCB0aGUgYGBgaWZlbHNlYGBgIGZ1bmN0aW9uLiBUaGlzIGlzIGEgdmVyeSBjb21tb24gYW5kIGVmZmVjdGl2ZSBmdW5jdGlvbiB0aGF0IHJlYWRzIGBgYGlmZWxzZSh0ZXN0LGlmIFRSVUUsIGlmIEZBTFNFKWBgYC4gQnJva2VuIGRvd24gYSBsaXR0bGUgZnVydGhlciwgdGhlIGBgYHRlc3RgYGAgaXMgYSBsb2dpY2FsIG9wZXJhdGlvbiB3aGVyZSB3ZSBzYXkgaWYgdGhlIHN0YXRlbWVudCAiYGBgZGF0YSRHZW5kZXJgYGAgZXF1YWxzIChgYGA9PWBgYCkgTSIgaXMgVFJVRS4gVGhlIG5leHQgdmFsZXMgYGBgaWYgVFJVRWBgYCBhbmQgYGBgaWYgRkFMU0VgYGAgc2ltcGx5IHNheSB3aGF0IHdlIHdhbnQgdGhvc2UgcmVzcGVjdGl2ZSB2YWx1ZXMgdG8gYmUgYWZ0ZXIgdGhpcyBpcyBydW4uIA0KDQpUbyBwdXQgaXQgYWxsIHRvZ2V0aGVyIHdlJ3JlIHNheWluZywgImNyZWF0ZSBhIG5ldyB2YXJpYWJsZSwgYGBgbWFsZV9kYGBgIGluIGRhdGFmcmFtZSBgYGBkYXRhYGBgLCB3aGVyZSBpZiBgYGBkYXRhJEdlbmRlcmBgYCBzYXlzICJNIiwgcmVjb2RlIHRoYXQgaW50byBgYGAxYGBgLCBmb3IgYW55dGhpbmcgZWxzZSwgcmVjb2RlIHRob3NlIGludG8gYGBgMGBgYC4NCg0KIyMjIFJlY29kaW5nIHZhcmlhYmxlIGludG8gZGlzdGluY3QgZ3JvdXBzDQoNCkxldCdzIHRha2UgYSBsb29rIGF0IGEgc2l0dWF0aW9uIHdoZXJlIHlvdSBoYXZlIGEgY29udGludW91cyB2YWx1ZSB0aGF0IHlvdSB3YW50IHRvIGdyb3VwIGludG8gYSBmZXcgZGlzdGluY3QgY2F0ZWdvcmllcy4gV2UnbGwgc2hpZnQgb3VyIGF0dGVudGlvbiB0byBhIHZhcmlhYmxlIGF0IHRoZSB2ZXJ5IGVuZCBgYGBPUWBgYC4gDQoNCkZpcnN0IHRoaW5ncyBmaXJzdCwgbGV0cyBnZXQgYW4gaWRlYSBvZiB3aGF0IHdlJ3JlIHdvcmtpbmcgd2l0aDogDQpgYGB7cn0NCnN1bW1hcnkoZGF0YSRPUSkNCmBgYA0KV2UncmUgZ29ubmEgZ3JvdXAgdGhpcyB2YXJpYWJsZSBpbnRvIDQgY2F0ZWdvcmllcyB1c2luZyB0aGUgYGBgY2FzZV93aGVuYGBgIGZ1bmN0aW9uIGZyb20gdGhlIGBgYGRwbHlyYGBgIHBhY2thZ2UuIFRoaXMgcGFja2FnZSBpcyBwYXJ0IG9mIHRoZSBgYGB0aWR5dmVyc2VgYGAgZmFtaWx5ICh0aGF0J3MgYSB3aG9sZSBvdGhlciB0b3BpYykuIEZvciBub3cgbGV0cyBnZXQgaW50byBzb21lIGNvZGUuIA0KDQpgYGB7cn0NCmxpYnJhcnkodGlkeXZlcnNlKQ0KYGBgDQoNCiBXZSdyZSBnb25uYSBkaXZpZGUgdGhlIDQgY2F0ZWdvcmllcyB1c2luZyB0aGUgNCBxdWFydGlsZXMgYXMgbGlzdGVkIGFib3ZlLiANCg0KYGBge3J9DQpkYXRhJE9RX2NhdDwtIGNhc2Vfd2hlbigNCiAgZGF0YSRPUTw9MjkgfiIxIiwNCiAgZGF0YSRPUT49MzAmZGF0YSRPUTw9MzQgfiIyIiwNCiAgZGF0YSRPUT49MzUmZGF0YSRPUTw9NDcgfiIzIiwNCiAgZGF0YSRPUT49NDggfiI0Ig0KKQ0KDQpkYXRhJE9RX2NhdDwtYXMubnVtZXJpYyhkYXRhJE9RX2NhdCkNCmBgYA0KDQpOb3cgaXMgcHJvYmFibHkgYSBnb29kIHRpbWUgdG8gdGFsayBhYm91dCBsb2dpY2FsIG9wZXJhdG9ycy4gSW4gdGhlIGFib3ZlIGNvZGUgeW91IHNlZSBhIGxvdCBvZiByYW5kb20gc3ltYm9scyB0aGF0IGFyZSBwcm9iYWJseSBzY2FyaW5nIGEgbG90IG9mIHBlb3BsZSBvZmYuIEJ1dCBpZiB5b3UganVzdCBiZWFyIHdpdGggbWUgZm9yIGEgZmV3IG1vcmUgc2VudGVuY2VzLCBJJ2xsIGV4cGxhaW4gZXZlcnl0aGluZy4gDQpXZSBzaG91bGQgYWxsIGJlIGZhbWlsaWFyIHdpdGggc29tZSBvZiB0aGVzZSwgYnV0IHRoZXkgY291bGQgc3RpbGwgYmUgYSBsaXR0bGUgY3J5cHRpYzoNCg0KYGBgPmBgYCBncmVhdGVyIHRoYW4gIA0KYGBgPj1gYGAgZ3JlYXRlciB0aGFuIG9yIGVxdWFsIHRvICANCmBgYDxgYGAgbGVzcyB0aGFuICANCmBgYDw9YGBgIGxlc3MgdGhhbiBvciBlcXVhbCB0byAgDQpgYGA9PWBgYCBpcyBlcXVhbCB0byAgDQpgYGAhPWBgYCBpcyBub3QgZXF1YWwgdG8gIA0KDQpgYGAmYGBgIGFuZCAgDQpgYGB8YGBgIG9yICANCg0KV2l0aCBvdXIgbmV3Zm91bmQga25vd2xlZGdlLCBsZXQncyB0YWtlIGEgY2xvc2VyIGxvb2sgYXQgaG93IHdlIGNyZWF0ZWQgY2F0ZWdvcnkgMi4gYGBgZGF0YSRPUT49MzAmZGF0YSRPUTw9MzQgfiAiMiJgYGAgIA0KSWYgd2UgdGFrZSBpcyBzbG93LCB3ZSBjYW4gc2VlIHdlIHNwZWNpZmllZCAiaWYgT1Egc2NvcmUgaXMgZ3JlYXRlciB0aGFuIG9yIGVxdWFsIHRvIDMwKDEgYWJvdmUgMXN0IHF1YXJ0aWxlKSBBTkQgT1Egc2NvcmUgaXMgbGVzcyB0aGFuIG9yIGVxdWFsIHRvIDM0IChtZWRpYW4pLCB0aGVuIHJlY29kZSBpbnRvIDIiDQoNClRoZW4gdGhlIGxhc3QgbGluZSBJJ20ganVzdCBjaGFuZ2luZyB0aGUgdmFyaWFibGUgdHlwZSBmcm9tIGNoYXJhY3RlciB0byBudW1lcmljLiBJZiBhIHZhcmlhYmxlIGlzIHN0b3JlZCBhcyBudW1lcmljIGFuZCB3ZSB3YW50ZWQgdG8gY29udmVydCBpdCB0byBjaGFyYWN0ZXIsIHdlIGNvdWxkIGNhbGwgYGBgYXMuY2hhcmFjdGVyKClgYGANCg0KIyMgU3Vic2V0dGluZyBEYXRhc2V0cw0KDQpJJ20gbm90IGFjdHVhbGx5IHN1cmUgaWYgeW91IGNvdWxkIGRvIHRoaXMgaW4gU1BTUyBvciBub3Qgd2l0aG91dCBqdW1waW5nIHRocm91Z2ggbXVsdGlwbGUgaG9vcHMuIFRoZXJlJ3MgbWFueSB3YXlzIHlvdSBtaWdodCB3YW50IHRvIHN1YnNldCBhIGRhdGFzZXQsIHNvIEknbGwgc2hvdyB0aGUgbW9zdCBjb21tb24uIA0KDQojIyMgS2VlcGluZyBzcGVjaWZpYyB2YXJpYWJsZXMNCg0KQSBsb3Qgb2YgdGltZXMgeW91J2xsIGFjcXVpcmUgYSBnYXJnYW50dWFuIG9mIGEgZGF0YXNldCBhbmQgeW91IG9ubHkgbmVlZCBhIGZldyB2YXJpYWJsZXMuIEkgcGVyc29uYWxseSBoYXRlIGhhdmluZyBhIGJ1bmNoIG9mIHZhcnMgaW4gbXkgZGF0YXNldCB0aGF0IEknbSBub3QgZXZlbiBnb25uYSB1c2UuIFdlJ2xsIHVzZSB0aGUgYGBgc3Vic2V0KClgYGAgZnVuY3Rpb24gdG8gdHJpbSBvdXIgb3JpZ2luYWwgZGF0YXNldCBgYGBkYXRhYGBgIGludG8gc29tZXRoaW5nIG1vcmUgbWFuYWdlYWJsZS4gQXMgYWx3YXlzLCBsZXQncyBmaXJzdCBnZXQgYSBnb29kIGxvb2sgYXQgb3VyIGN1cnJlbnQgZGF0YXNldDoNCg0KYGBge3J9DQpwaWxsYXI6OmdsaW1wc2UoZGF0YSkNCmBgYA0KTm93IHRoYXQgd2Uga25vdyB3aGF0IHdlJ3JlIHdvcmtpbmcgd2l0aCwgbGV0J3MgdXNlIHRoZSBgYGBzdWJzZXQoKWBgYCBmdW5jdGlvbiB0byBrZWVwIG9ubHkgcmVsZXZhbnQgdmFyaWFibGVzOg0KDQpgYGB7cn0NCm5ld2RhdGE8LSBzdWJzZXQoZGF0YSwgc2VsZWN0ID0gYyhDbGllbnRfSUQsRE9CLEdlbmRlcixFdGhuaWNpdHksT1EsdHN0MTp0c3QxMCkpDQoNCnBpbGxhcjo6Z2xpbXBzZShuZXdkYXRhKQ0KYGBgDQpUaGUgYGBgc3Vic2V0KClgYGAgZnVuY3Rpb24gdXNlcyAyIHByaW1hcnkgYXJndW1lbnRzLCB0aGUgZmlyc3QgaXMgdGhlIG9yaWdpbmFsIGRhdGFzZXQ7IGluIG91ciBjYXNlLCBpdCB3YXMgYGBgZGF0YWBgYC4gVGhlIG5leHQgYXJndW1lbnQgaXMgdGhlIGNyaXRlcmlhIGZvciBzdWJzZXR0aW5nLiBJbiB0aGlzIGZpcnN0IGNhc2UsIGl0IHdhcyBzZWxlY3Rpbmcgc3BlY2lmaWMgdmFyaWFibGVzLiAgDQoNCkxldCdzIHNheSB3ZSBvbmx5IHdhbnQgdG8gZ2V0IHJpZCBvZiBhIGNvdXBsZSB2YXJpYWJsZXMuIEl0IHdvdWxkIGJlIHJlYWxseSB0aW1lIGNvbnN1bWluZyAoYW5kIG1pc3Rha2UgcHJvbmUpIHRvIHdyaXRlIG91dCAyMCBkaWZmZXJlbnQgdmFyaWFibGVzIHRoYXQgd2Ugd2FudGVkIHRvIGtlZXAuIFdlIHdvdWxkIGluc3RlYWQgYWRkIGEgYGBgLWBgYCBpbiBmcm9udCBvZiBgYGBjKClgYGAgYW5kIGVudGVyIHRoZSB2YXJpYWJsZXMgd2Ugd291bGQgbGlrZSB0byBleGNsdWRlLiBJdCB3b3VsZCBsb29rIHNvbWV0aGluZyBsaWtlIHRoaXM6ICANCmBgYCBuZXdkYXRhPC1zdWJzZXQoZGF0YSwgc2VsZWN0ID0gLWMoU0ksIE9RX2NhdCkpYGBgDQoNCiMjIyBLZWVwaW5nL2Rpc2NhcmRpbmcgcm93cyANCg0KSWYgd2Ugd2FudCB0byBrZWVwIHBhcnRpY2lwYW50cyBiYXNlZCBvbiB0aGVpciBsZXZlbCBvbiBhIGdpdmVuIHZhcmlhYmxlLCB3ZSBjYW4gYWxzbyB0dXJuIHRvIHRoZSBgYGBzdWJzZXQoKWBgYCBmdW5jdGlvbjoNCg0KYGBge3J9DQojIyMgS2VlcGluZyBvbmx5IGZlbWFsZSBwYXJ0aWNpcGFudHMNCmZlbWFsZV9kcyA8LSBzdWJzZXQoZGF0YSwgZGF0YSRHZW5kZXI9PSJGIikNCg0KI05vdGljZSB0aGUgZGlmZmVyZW5jZSBpbiB0aGUgR2VuZGVyIGNvdW50cyBiZXR3ZWVuIGRhdGEgJiBmZW1hbGVfZHMNCnRhYmxlKGRhdGEkR2VuZGVyKQ0KdGFibGUoZmVtYWxlX2RzJEdlbmRlcikNCg0KIyMgS2VlcGluZyBwYXJ0aWNpcGFudHMgd2l0aCBzY29yZXMgYmV0d2VlbiAyMC0zMCBvbiBPUQ0KT1FfZGF0IDwtIHN1YnNldChkYXRhLCBkYXRhJE9RPj0yMCAmIGRhdGEkT1E8PTMwKQ0KDQojIE5vdGljZSB0aGUgbWluLiAmIG1heC4gZm9yIGJvdGgNCnN1bW1hcnkoZGF0YSRPUSkNCnN1bW1hcnkoT1FfZGF0JE9RKQ0KYGBgDQogDQojIyMjIEtlZXBpbmcgc3BlY2lmaWMgcm93cyAmIHJhbmRvbWx5IHNhbXBsaW5nIHBhcnRpY2lwYW50cw0KDQpUaGlzIG9uZSdzIHByZXR0eSBjb21tb24gd2hlbiB5b3Ugd2FudCB0byBrZWVwIGEgaG9sZG91dCB2YWxpZGF0aW9uIHNldCAoaWYgYW55b25lJ3MgaW50byBtYWNoaW5lIGxlYXJuaW5nKS4gIA0KICANCkxldCdzIHNheSB3ZSB3YW50IHRvIHVzZSBqdXN0IHRoZSBmaXJzdCA1MDAgcm93cyBvZiBgYGBkYXRhYGBgLiBGaXJzdCBsZXQncyBsb29rIGF0IGhvdyBtYW55IHJvd3MgZGF0YSBoYXM6IA0KYGBge3J9DQpucm93KGRhdGEpDQpgYGANCk5leHQsIGxldHMgc3Vic2V0IHVzaW5nIHRoZXNlIGhhbmR5LWRhbmR5IGBgYFtdYGBgOg0KDQpgYGB7cn0NCmZpcnN0NTAwPC0gZGF0YVsxOjUwMCxdDQpucm93KGZpcnN0NTAwKQ0KYGBgDQpUaGUgYnJhY2tldHMgYXJlIHByZWRvbmluYW50bHkgdXNlZCBpbiBSIChzcGVjaWZpY2FsbHkgd2hlbiB3b3JraW5nIHdpdGggZGF0YWZyYW1lcykgdG8gcmVmZXJlbmNlIHJvd3MgYW5kIGNvbHVtbnMgaW4gdGhlIGRhdGFzZXQuIFRoZSBhcmd1bWVudCBpbiBmcm9udCBvZiB0aGUgY29tbWEgKDE6NTAwKSBpcyBpbiByZWZlcmVuY2UgdG8gdGhlIHJvd3MuIEhlcmUgd2Ugd2FudGVkIHJvd3MgMSB0aHJvdWdoIDUwMC4gV2UgYWxzbyB3YW50ZWQgdG8ga2VlcCBhbGwgdGhlIGNvbHVtbnMsIHNvIHdlIGRpZG4ndCBzcGVjaWZ5IGFueXRoaW5nIGFmdGVyIHRoZSBjb21tYS4gc2F5IHdlIHdhbnRlZCB0byBrZWVwIHRoZSBmaXJzdCA1MDAgcm93cyBhbmQgdGhlIGZpcnN0IDEwIGNvbHVtbnMuIE91ciBjb2RlIHdvdWxkIGxvb2sgc29tZXRoaW5nIGxpa2UgdGhpczoNCmBgYHtyfQ0KZmlyc3Q1MDA8LWRhdGFbMTo1MDAsMToxMF0NCmBgYA0KDQojIyMjIFNwbGl0LWhhbGYgUmFuZG9tIFNhbXBsZQ0KSW5zdGVhZCBvZiBqdXN0IHRha2luZyB0aGUgZmlyc3QgNTAwIG9yIHNvIHJvd3MsIHdlIGNhbiByYW5kb21seSBzYW1wbGUgb3VyIGRhdGFzZXQgbGlrZSBzbzoNCmBgYHtyfQ0Kc2V0LnNlZWQoMTIzNCkNCnNwbHQ8LXNhbXBsZShucm93KGRhdGEpLG5yb3coZGF0YSkqLjUpDQoNCmhhbGZfZGF0PC1kYXRhW3NwbHQsXQ0KDQpucm93KGhhbGZfZGF0KQ0KYGBgDQoNCkkga25vdywgSSBrbm93LCBJIGhhdmUgc29tZSBleHBsYWluaW5nIHRvIGRvLi4uICANCg0KRmlyc3Qgb2ZmLCBgYGBzZXQuc2VlZCgxMjM0KWBgYCBpcyB1c2VkIHdoZW4geW91J3JlIGRvaW5nIHNvbWV0aGluZyByYW5kb20gaW4gUiAoaS5lLiByYW5kb20gc2FtcGxpbmcsIHJ1bm5pbmcgYSBtYWNoaW5lIGxlYXJuaW5nIGFsZ28sIGV2ZW4gdXNpbmcgbWF4aW11bSBsaWtlbGlob29kIG9yIEJheWVzaWFuKSBhbmQgeW91IHdhbnQgdG8gcmVwbGljYXRlIHlvdXIgcmVzdWx0cy4gIA0KDQpUaGUgbmV4dCBsaW5lIGlzIHNpbXBseSByYW5kb21seSBnZW5lcmF0aW5nIGEgc2FtcGxlIG9mIG51bWJlcnMgYmV0d2VlbiAxIGFuZCB0aGUgbnVtYmVyIG9mIHJvd3MgaW4gYGBgZGF0YWBgYCAoMjQwOCkuIFRoaXMgaXMgd2hhdCB0aGUgZmlyc3QgYXJndW1lbnQgYGBgbnJvdyhkYXRhKWBgYCBtZWFucy4gVGhlIHNlY29uZCBhcmd1bWVudCBgYGBucm93KGRhdGEpKi41YGBgLCBtZWFucyBtYWtlIHRoZSBzYW1wbGUgaGFsZiB0aGUgbnVtYmVyIG9mIHJvd3Mgb2YgZGF0YSwgZGF0YSByb3dzICogLjUgIA0KDQpOb3cgdGhhdCB3ZSBoYXZlIGEgcmFuZG9tIHNhbXBsZSBvZiBkaWdpdHMgdGhhdCBpcyBlcXVhbCB0byBoYWxmIG9mIGBgYGRhdGFgYGAsIHdlIGFyZSBnb25uYSBtYWtlIHRob3NlIGRpZ2l0cyByZXByZXNlbnQgdGhlIGV4YWN0IHJvd3Mgd2Ugd2FudCB0byBrZWVwIGZyb20gYGBgZGF0YWBgYC4gUmVtZW1iZXIgdGhlIGJyYWNrZXRzIGBgYFtdYGBgPyBXZSB1c2VkIHRoZSBzYW1lIHByaW5jaXBsZSBmcm9tIGFib3ZlIHdoZXJlIHdlIGtlcHQgcm93cyAxOjUwMCwgZXhjZXB0IHdlIHVzZWQgb3VyIHJhbmRvbSBzYW1wbGUgd2UgY3JlYXRlZCwgYW5kIHNwZWNpZmllZCB3ZSB3YW50ZWQgdG8ga2VlcCBhbGwgdGhlIGNvbHVtbnMuICANCg0KTmV4dCB0aGluZyB3ZSB3b3VsZCB3YW50IHRvIGRvIGlzIHJlY292ZXIgdGhlIG90aGVyIGhhbGYgb2YgdGhlIGRhdGFzZXQgdGhhdCBpc24ndCByZXByZXNlbnRlZCBpbiBgYGBoYWxmX2RhdGBgYC4gVG8gZG8gdGhpcywgeW91IHNpbXBseToNCg0KYGBge3J9DQpvdGhlcl9oYWxmPC0gZGF0YVstc3BsdCxdDQpgYGANCg0KTm93IHlvdSBoYXZlIGEgY29tcGxldGVseSBjb21wbGltZW50YXJ5IGRhdGFzZXQuIA0KDQojIyBSYW5kb20gVGlkYml0cw0KDQpBcyBJIHdpbmQgZG93biB0aGlzIGFydGljbGUgSSdsbCBnaXZlIGEgZmV3IHJhbmRvbSB0aWRiaXRzIHRvIGhlbHAgeW91IGNsZWFuIHlvdXIgZGF0YS4gDQoNCiMjIyBXb3JraW5nIHdpdGggZGF0ZXMNCg0KV2UgaGF2ZSBhIHZhcmlhYmxlIGBgYERPQmBgYCB0aGF0IHJlcHJlc2VudHMgRGF0ZSBvZiBCaXJ0aC4gVW5mb3J0dW5hdGVseSwgd2UgZG9uJ3QgaGF2ZSBhZ2UsIGJ1dCB3ZSBjYW4gY2FsY3VsYXRlIGl0IHdpdGggcmVsYXRpdmUgZWFzZS4gDQoNCkZpcnN0LCBsZXQncyB0YWtlIGEgbG9vayBhdCB3aGF0IGtpbmQgb2YgdmFyaWFibGUgUiB0aGlua3MgaXQgaXMNCmBgYHtyfQ0KY2xhc3MoZGF0YSRET0IpDQpgYGANCkFzIEkgc3VzcGVjdGVkIFIgZG9lc24ndCB0aGluayB0aGlzIGlzIGEgZGF0ZSwgd2hpY2ggbWFrZXMgaXQgdG91Z2ggdG8gY2FsY3VsYXRlIGFnZS4gV2UncmUgZ29ubmEgaGF2ZSB0byBjaGFuZ2UgdGhlIHR5cGUgYmVmb3JlIHdlIGRvIGFueXRoaW5nLiANCg0KUidzIGRhdGUvdGltZSByZXByaXRvaXJlIGlzIG5vdCBxdWl0ZSBhcyBzb3BoaXN0b2NhdGVkIGFzIFNBUywgYnV0IGl0IGNhbiBkbyB0aGUgam9iLiBSJ3MgYmFzZSBwYWNrYWdlIGhhcyB0aGUgZnVuY3Rpb24gYGBgYXMuRGF0ZSgpYGBgIHRoYXQgaGVscHMgdXMgb3V0LiBMZXQncyB0YWtlIGEgbG9vayBhdCBob3cgd2UgY2FuIGNvbnZlcnQgdGhlIGRhdGUgaW4gb3VyIGRhdGFzZXQgdG8gYSBmb3JtYXQgdGhhdCBSIGNhbiByZWNvZ25pemU6DQoNCmBgYHtyfQ0KZGF0YSRET0I8LSBhcy5EYXRlKGRhdGEkRE9CLCBmb3JtYXQgPSAiJW0vJWQvJVkiKQ0KDQpjbGFzcyhkYXRhJERPQikNCmBgYA0KVGhlIGBgYGZvcm1hdCA9YGBgIGFyZ3VtZW50IGFsbG93cyB5b3UgdG8gc3BlY2lmeSBob3cgeW91ciBkYXRlIGlzIGN1cnJlbnRseSBmb3JtYXR0ZWQuIFNpbmNlIG91cnMgd2FzIG1tL2RkL3l5eXksIHdlIHVzZWQgIiVtLyVkLyVZIi4gSWYgb3VyIGZvcm1hdCB3YXMgIm1tLWRkLXl5IiBmb3IgZXhhbXBsZSwgaXQgd291bGQgcmVhZCAiJW0tJWQtJXkiLiAgDQoNCkNvb2whIE5vdyB3ZSBjYW4gY2FsY3VsYXRlIGFnZS4gV2UnbGwgdXNlIHRoZSBgYGBsdWJyaWRhdGVgYGAgcGFja2FnZSB0byBoZWxwIHVzIG91dA0KDQpgYGB7cn0NCmRhdGEkYWdlPC0gbHVicmlkYXRlOjppbnRlcnZhbChkYXRhJERPQixTeXMuRGF0ZSgpKSUvJW1vbnRocygxMikNCg0Kc3VtbWFyeShkYXRhJGFnZSkNCmBgYA0KVGhlIHBhY2thZ2UgYGBgbHVicmlkYXRlYGBgIGhhcyBtYW55IHVzZWZ1bCBmdW5jdGlvbnMgdGhhdCBJIHdvbid0IGdldCBpbnRvIGhlcmUsIGJ1dCBqdXN0IGtub3cgdGhleSdyZSB0aGVyZS4gSWYgeW91IGV2ZXIgd2FudCB0byBleHBsb3JlIGEgcGFja2FnZSBvciBmdW5jdGlvbiBhbnkgZnVydGhlciwganVzdCBydW4gdGhlIGNvbW1hbmQgYGBgaGVscChsdWJyaWRhdGUpYGBgLiAgDQpUaGUgaW50ZXJ2YWwgZnVuY3Rpb24gYWxsb3dzIHlvdSB0byBjYWxjdWxhdGUgYW4gaW50ZXJ2YWwgYmV0d2VlbiAyIHRpbWVwb2ludHMuIFRoZSBgYGAlLyVgYGAgaXMgd2hhdCB3ZSBjYWxsIGEgInBpcGUgb3BlcmF0b3IiIGFuZCBpcyBhIHN0YXBsZSBpbiB0aGUgYGBgdGlkeXZlcnNlYGBgLiBJdCBwcm92aWRlcyBhIHNpbXBsZSB3YXkgdG8gcGVyZm9ybSBhIG1hdGhlbWF0aWNhbCBvcGVyYXRpb24gb24gdGhlIHJlc3VsdHMgb2YgYSBmdW5jdGlvbiB3aXRoIGp1c3QgMSBsaW5lIG9mIGNvZGUuIEhlcmUgd2hhdCB3ZSdyZSB0ZWxsaW5nIGl0IHRvIGRvIGlzIGNhbGN1bGF0ZSB0aGUgaW50ZXJ2YWwgYmV0d2VlbiBlYWNoIHBhcnRpY2lwYW50J3MgRE9CIGFuZCBkaXZpZGUgdGhhdCBpbnRlcnZhbCBieSAxMiBtb250aHMsIHdoaWNoIHdpbGwgZ2l2ZSB1cyB0aGUgYWdlIG9mIGVhY2ggcGFyaXRpY3BhbnQuIEZyb20gaGVyZSB3ZSBjYW4gZW50ZXIgaXQgaW50byBvdXIgZXF1YXRpb24gbGlrZSB3ZSB3b3VsZCBhbnkgbm9ybWFsIHZhcmlhYmxlLiANCg0KIyMjIFN1YnN0cmluZw0KDQpTdWJzdHJpbmcgbWF5IGNvbWUgaW4gaGFuZHkgaWYgeW91IHdhbnQgdG8gdGFrZSBhIHNuaXBwZXQgb2YgYSB2YXJpYWJsZS4gVG8gYmUgaG9uZXN0LCBJIHByaW1hcmlseSB1c2UgdGhpcyB3aGVuIEkgZ2V0IGEgZGF0ZS90aW1lIHZhcmlhYmxlIGFuZCBJIGp1c3Qgd2FudCB0byBrZWVwIHRoZSBkYXRlLiBUbyBpbGx1c3RyYXRlIHRoaXMgcG9pbnQsIEknbSBnb2luZyB0byBtYWtlIGEgdmFyaWFibGUgaW4gYGBgZGF0YWBgYCB0aGF0IGhhcyB0aGUgY3VycmVudCBkYXRlL3RpbWUgYW5kIHN1YnNldCBmcm9tIHRoZXJlLg0KDQpgYGB7cn0NCmRhdGEkY3VycmVudCA8LSByZXAoU3lzLnRpbWUoKSwgbnJvdyhkYXRhKSkNCg0KZGF0YSRjdXJyZW50WzFdDQpgYGANCk5vdyB0aGF0IHdlJ3JlIGhlcmUsIEkgbWlnaHQgYXMgd2VsbCB0YWxrIGFib3V0IHRoZSBgYGByZXBgYGAgY29tbWFuZC4gVGhpcyBzdGFuZHMgZm9yICJyZXBlYXQiIGFuZCBpcyBhIGdvb2Qgd2F5IHRvIGNyZWF0ZSByZXBldGl0aW9ucyBpbiBSLiBSaWdodCBub3csIEknbSBzYXlpbmcgbWFrZSBhIG5ldyB2YXJpYWJsZSBjYWxsZWQgYGBgY3VycmVudGBgYCBhbmQgZmlsbCBpdCB3aXRoIGEgc2VyaWVzIG9mIHRoZSBjdXJyZW50IGRhdGUgYW5kIHRpbWUsIGZvciB0aGUgbnVtYmVyIG9mIHJvd3MgaW4gYGBgZGF0YWBgYC4gIA0KDQpCYWNrIHRvIHN1YnN0cmluZy4gV2UncmUgZ29ubmEgdXNlIHRoZSBjb21tYW5kIGBgYHN1YnN0cigpYGBgIHRvIGtlZXAgb25seSB0aGUgZGF0ZSAoMjAyMS0xMi0wNykuIFRoZSBhcmd1bWVudHMgcmVhZDogMSkgd2hhdCBkbyB3ZSB3YW50IHRvIHN1YnNldCAyKSBhdCB3aGljaCBjaGFyYWN0ZXIgZG8gd2Ugc3RhcnQgMykgYXQgd2hpY2ggY2hhcmFjdGVyIGRvIHdlIHN0b3AuIFNpbmNlIHRoZSBkYXRlIHN0YXJ0cyBhdCAxIGFuZCBlbmRzIGF0IDEwLCB3ZSdsbCBnbyBhaGVhZCBhbmQgcHV0IHRob3NlIGFyZ3VtZW50cyBpbi4gDQpgYGB7cn0NCmRhdGEkY3VycmVudF9kYXRlPC0gc3Vic3RyKGRhdGEkY3VycmVudCwxLDEwKQ0KDQpkYXRhJGN1cnJlbnRfZGF0ZVsxXQ0KYGBgDQogDQojIyBTUFNTIFJlaW50ZWdyYXRpb24NCg0KTGFzdCB0aGluZyB3ZSBuZWVkIHRvIGRvIGlzIHJld3JpdGUgeW91ciBkYXRhIGJhY2sgaW50byBTUFNTIGZvcm1hdC4gVGhpcyBpcyBhIDEgbGluZSBjb21tYW5kIHRoYXQnbGwgYWxsb3cgeW91IHRvIHRha2UgeW91ciBjbGVhbmVkIGRhdGEgZnJvbSBSIGFuZCByZWFkIGl0IGJhY2sgaW4gdG8gU1BTUy4NCmBgYHtyfQ0KaGF2ZW46OndyaXRlX3NhdihuZXdkYXRhLCAibmV3ZGF0YS5zYXYiKQ0KYGBgDQpZb3Ugc2hvdWxkIHNlZSBhIGZpbGUgdGl0bGVkICJuZXdkYXRhIiBpbiB0aGUgZm9sZGVyIHlvdSd2ZSBiZWVuIHdvcmtpbmcgb3V0IG9mLiBJZiBub3QsIG1ha2Ugc3VyZSB0byBnbyBiYWNrIGFuZCBzZXQgeW91ciB3b3JraW5nIGRpcmVjdG9yeSBiYWNrIHRvIHRoZSBmb2xkZXIgb2YgeW91ciBjaG9pY2UuIA0KDQojIyBDb25jbHVzaW9uDQoNClRoYXQncyBnb25uYSBkbyBpdCBmb3IgdGhpcyBwYWdlLiBSZW1lbWJlciwgZGF0YSBjbGVhbmluZyBpcyBhIHByb2Nlc3Mgc28geW91J2xsIG1vc3QgbGlrZWx5IGhhdmUgdG8gZG8gYSBmZXcgb2YgdGhlc2Ugc3RlcHMgb3Igc2xpZ2h0IHZhcmlhdGlvbnMgb24gbXkgcHJvdmlkZWQgY29kZS4gTGFzdCBiaXQgb2YgYWR2aWNlLCBHb29nbGUgaGFzIGJlZW4gbXkgYmVzdCBmcmllbmQgc2luY2UgSSd2ZSBzdGFydGVkIGxlYXJuaW5nIFIuIFlvdSBjYW4gZmluZCBhIHNvbHV0aW9uIHRvIGFsbW9zdCBhbnkgZXJyb3Igd2l0aCBhIHF1aWNrIHNlYXJjaC4gQW5kIGFzIGFsd2F5cywgaWYgeW91IGFyZSBzdGlsbCBoYXZpbmcgdHJvdWJsZSBvciB3YW50IG1lIHRvIGNvdmVyIGFueXRoaW5nIGVzbGUsIGZlZWwgZnJlZSB0byBzaG9vdCBtZSBhbiBbZW1haWwgYXQgc2NjQHBhbG9hbHRvdS5lZHVdKG1haWx0bzogc2NjQHBhbG9hbHRvdS5lZHUpIA0KDQpCeTogSm9lIFJhem8NCiAgICBTQ0MgU3R1ZGVudCBEaXJlY3Rvcg0KICAgIERlY2VtYmVyLCAyMDIxDQo=