Making Sense of Survey Data

Now that we have cleaned up our survey data, we can start asking questions about it! By the end of the previous session, Collecting and Cleaning Survey Data, we generated two cleaned datasets:

  • survey_data_tidy.csv
  • survey-data_wide.csv

Long, or “tidy” data and wide data each have their pros and cons, and in this session we’re going to explore the benefits and drawbacks of each as we explore our data. But first, let’s take a moment to do some thinking:

To begin, let’s open up our .RProj file, so that our working directory will be captured. Now, let’s create a new R script, and call it survey_analysis_script.R.

Load the Tidyverse

Now that we’ve started a new script, we need to load the libraries that we’ll be using, which in this case is just the Tidyverse.

library(tidyverse)

Load the data

We can now load the two datasets that were generated in the previous session:

survey_data_tidy <- read_csv("survey_data_tidy.csv")
survey_data_wide <- read_csv("survey_data_wide.csv")
  1. Take a look at survey_data_tidy and survey-data_wide using the View() command.
View(survey_data_tidy)
View(survey_data_wide)
  1. In groups, spend a few minutes talking about some things you’d like to know about the data (ie. groups you’d like to count, possible relationships, etc.).

  2. Taking a closer look at the tidy and wide datasets, can you think of reasons why certain questions might be better with either one of the datasets?

Exploring Wide Data

Wide data is easier for humans to read and interpret, and lends itself well to asking simple questions from single variables, or columns, of data. Tidy data is better for asking more complicated questions, as well as asking about multiple variables and possible relationships between them. Let’s first start with some simple questions we can ask from the wide version of our data.

A simple first step when exploring a dataset is getting counts of individual variables. To do this, we can use the count() function:

What does the sample breadkdown of gender?

survey_data_wide |>
  count(gender)

Step-by-step explanation:

  1. We start with the survey_data_wide object and use the pipe, |>, to indicate we want to do something with it.
  2. We then use the count() function to give us a total sum of whatever variable/column name we put inside the brackets.

What is the sample breakdown of age?

survey_data_wide |>
  count(age)

In addition to looking at single variables with the count() function, we can also generate quick summary tables across multiple variables by separating them with commas ,:

What is the sample breakdown of age and gender?

survey_data_wide |>
  count(age, gender)
## # A tibble: 24 × 3
##      age gender               n
##    <dbl> <chr>            <int>
##  1    18 Female              31
##  2    18 Male                34
##  3    18 Non-binary/Other     3
##  4    19 Female              25
##  5    19 Male                24
##  6    19 Non-binary/Other     6
##  7    20 Female              28
##  8    20 Male                17
##  9    20 Non-binary/Other     3
## 10    21 Female              12
## # ℹ 14 more rows

You’ll see the note that there are 14 more rows, and that you can use the print() function to view more. You can do this you can do this by specifiying the amount of rows (in this case there are 24)

survey_data_wide |>
  count(age, gender) |>
  print(n = 24)

You can keep adding additional variables to these, noting that each variable will add additional rows. If you don’t want to look at the values in the R console, you can use the View() function to open it in a separate tab.

survey_data_wide |>
  count(age, gender, year_of_study) |>
  View()

If this is something you want to come back to, you can save it into an R object.

demographics_count <- survey_data_wide |>
  count(age, gender, year_of_study)

View(demographics_count)

A final thing that can be helpful with the count() function is adding in the proportion that each sum represents in the total sample.

survey_data_wide |>
  count(gender) |>
  mutate(prop = n / sum(n))

Step-by-step explanation:

  1. Start by piping the survey_wide object to the count() function like we’ve been doing.
  2. Add another pipe to indicate we are passing that count to another function.
  3. mutate(prop creates a new column called prop, which stands for proportion (you can name this whatever you want).
  4. Everything on the right side of the = is assigned to the new prop column.
  5. n is the count for each gender category, and sum(n) is the total number of every row/person in the sample.
  6. n / sum(n)) calculates "count of each category" / "total count", which gives a proportion.

Your Turn!

See if you can create the following from the survey_data_wide object:

  1. A count of each year_of_study category.
survey_data_wide |>
  count(year_of_study)
  1. A count of each year_of_study and hours_per_day.
survey_data_wide |>
  count(year_of_study, hours_per_day)
  1. The count of year_of_study and its proportion.
survey_data_wide |>
  count(year_of_study) |>
  mutate(prop = n / sum(n))
  1. The count of year_of_study, hours_per_day, and the proportion
survey_data_wide |>
  count(year_of_study, hours_per_day) |>
  mutate(prop = n / sum(n))

Simple Barplots as Exploration

In addition to creating summary tables to explore data, creating simple plots can be another way to explore your data.

One way to do this, is using the base R barplot() function. This can be done in a few steps.

In this example, we’re going to plot the categories of the hours_per_day columns.

First, let’s look at the $ operator. When placed after a data object in R, it allows you to isolate specified columns.

survey_data_wide$hours_per_day

This will show all the values in that column in the R console. Give it a try with other columns!

Next, we can also use the table function here, to generate a quick summary table of that column.

table(survey_data_wide$hours_per_day)

Give this a try with some other columns!

Now we’re going to save this table as an object called hours_table.

hours_table <- table(survey_data_wide$hours_per_day)

Finally, we can feed the hours_table object into the barplot() function to get our plot.

barplot(hours_table)

You might notice that the bars aren’t in any particular order, which can be a bit annoying. Let’s clean this up by creating adding another step.

hours_sorted <- sort(hours_table, decreasing = TRUE)
barplot(hours_sorted)

The sort function is telling R that we want to reorder the values in hours_table, and the decreasing = TRUE allows us to see the values from biggest to smallest. You can change this to decreasing = FALSE to see smallest to biggest.

When exploring data, it’s not always necessary to add labels to our plots. However, this chart might be something you want to communicate, so let’s add some labels!

barplot(hours_sorted,
        main = "Hours per day on social media",
        xlab = "Hours per day",
        ylab = "Number of students")

With the barplot() function:

  • main is the title of the chart.
  • xlab is the label on the x (horizontal) axis.
  • ylad is the label on the y (vertical) axis.

Your Turn!

  1. Make a barplot for the feel_increase_stress variable, that orders the bars from biggest to smallest, and has labels for the title, and both axes.

Hint: break this into 3 steps:

  • Create an object that creates a table of the feel_increase_stress column
  • Create another object that sorts the table in decreasing order
  • Create a barplot and add 3 layers of labels.
stress_count <- table(survey_data_wide$feel_increase_stress)

stress_sorted <- sort(stress_count, decreasing = TRUE)

barplot(stress_sorted,
        main = "Social media makes me feel anxious or stressed",
        xlab = "Feeling anxious or stressed",
        ylab = "Number of students")
  1. Try this with another column!

Moving to Tidy Data

As we’ve seen, working with wide data can be helpful for generating quick summaries and counts. But what if we want to ask something that’s a bit more complicated?

In groups, take a look at the data and talk about how you might go about answering the question, which platforms are most used?

There are certainly ways to do this with the wide data. One way would be to use the count() function for each individual column. However, this is a bit clunky, and then you would have to then deal with merging the responses into a table (not impossible, but it’s more work). There is a way to do this is a single code chunk, but it becomes overly long and complicated:

platform_counts_wide <- survey_wide |>
  summarise(
    across(
      c(`LinkedIn`, Instagram, Reddit, Facebook,
        `X (Twitter)`, TikTok, Snapchat),
      ~ sum(.x))) |>
  pivot_longer(
    cols = everything(),
    names_to = "platform",
    values_to = "n_users") |>
  arrange(desc(n_users))

platform_counts_wide

We don’t need to understand this code chunk, it is just an example of what the code looks like. However, if we shift to tidy data, the code becomes much shorter and simpler.

First, let’s start by taking another look at the tidy data.

View(survey_data_tidy)

You can see that there is the single platforms column that we are able to query.

Now, let’s do a count of the different categories in this column.

survey_data_tidy |>
  count(platforms, name = "n_students")
## # A tibble: 7 × 2
##   platforms   n_students
##   <chr>            <int>
## 1 Facebook          3200
## 2 Instagram         5340
## 3 LinkedIn          2520
## 4 Reddit            4100
## 5 Snapchat          2420
## 6 TikTok            5480
## 7 X (Twitter)       2960

In this code, we are using the count() function like we just did, and the name = "n_students" creates a new column name for the number for each column. "n_students" was used to show this is the number of students, but you can put anything you want here, or leave it blank if preferred.

You can see that this code is very simple, but there is a big problem with the numbers! Because our tidy data adds additional rows to data, we are seeing numbers in the thousands when our sample size is only 300. However, when we cleaned the data, we created a unique ID for each entry. This was to help with re-identifying responses if needed, but it can also help us with eliminating redundant rows.

survey_data_tidy |>
  distinct(ID, platforms) |>
  count(platforms, name = "n_students")
## # A tibble: 7 × 2
##   platforms   n_students
##   <chr>            <int>
## 1 Facebook           160
## 2 Instagram          267
## 3 LinkedIn           126
## 4 Reddit             205
## 5 Snapchat           121
## 6 TikTok             274
## 7 X (Twitter)        148

The distinct() function is telling R that we only want to keep unique combinations of each student ID and platform, which eliminates any of the redundancy of the tidy data. As you can see, the numbers are now accurate to our sample.

As with before, it can be nice to arrange the numbers from biggest to smallest.

survey_data_tidy |>
  distinct(ID, platforms) |>
  count(platforms, name = "n_students") |>
  arrange(desc(n_students))
## # A tibble: 7 × 2
##   platforms   n_students
##   <chr>            <int>
## 1 TikTok             274
## 2 Instagram          267
## 3 Reddit             205
## 4 Facebook           160
## 5 X (Twitter)        148
## 6 LinkedIn           126
## 7 Snapchat           121

In this case, the arrange() function tells R that we want to change the order of something, and desc(n_students) specifies that we want the n_students column, which is the values for each platform, to be listed in descending order (biggest to smallest).

Finally, once we’re happy with the output of this code chunk, we can save it as an R object to revisit. We’ll call the object platform_count, and now we can easily view and work with it going forward.

platform_count <- survey_data_tidy |>
  distinct(ID, platforms) |>
  count(platforms, name = "n_students") |>
  arrange(desc(n_students))

Going Deeper with Tidy Data

Let’s keep digging into the tidy data, and start asking questions that are related to our bigger research question, “How does social media usage influence the mental health of university students?

Comparison Operators

Comparison operators allow us to look for specific criteria in our dataset. These return TRUE or FALSE based on whether the condition is met.

Operator Meaning Example Result
== Equal to 5 == 5 TRUE
!= Not equal to 5 != 3 TRUE
< Less than 3 < 5 TRUE
> Greater than 5 > 3 TRUE
<= Less than or equal to 3 <= 3 TRUE
>= Greater than or equal to 5 >= 3 TRUE

Once we understand these basic comparison operators, we can combine them using logical operators to create more complex filtering conditions.

Logical Operators

Logical operators allow us to filter data based on multiple conditions.

Operator Meaning Example Result
& Logical AND (Both conditions must be TRUE) (5 > 3) & (4 < 6) TRUE
| Logical OR (At least one condition must be TRUE) (5 > 3) | (4 > 6) TRUE
! Logical NOT (Reverses TRUE/FALSE) !(5 > 3) FALSE

Asking Questions with Filtering

The filter() function returns only the rows that meet a specified condition.

From the feel-question column in our tidy data, these values indicating whether social media makes students feel connected to others (feel_connected), anxious or stressed (feel_increase_stress), distracts them from academic work (feel_distracted), or improves their mood (feel_improved_mood).

As part of the bigger research question, “How does social media usage influence the mental health of university students?”, we can use this variable to start asking the question, “how many students feel X because of their social media use?

Let’s first start by asking, “how many students feel anxious or stressed because of social media?

stressed_count <- survey_data_tidy |>
  distinct(ID, feel_question, feel_response) |>
  filter(feel_question == "feel_increase_stress") |>
  count(feel_response, name = "feel_increase_stress") |>
  arrange(desc(feel_increase_stress))

stressed_count
## # A tibble: 5 × 2
##   feel_response     feel_increase_stress
##   <chr>                            <int>
## 1 Agree                               69
## 2 Disagree                            69
## 3 Neutral                             68
## 4 Strongly disagree                   48
## 5 Strongly agree                      46

Step-by-step explanation:

  • We start by creating a new R object that will save this information, called stressed_count.
  • We then pipe the survey_data_tidy object to indicate we will manipulate it in some way.
  • Next, distinct(ID, feel_question, feel_response) tells R to only keep unique combinations of ID, feel-question, and feel_response.
  • This is then piped through to the filter() command, and feel_question == feel_increase_stress looks for only for the stressed values in the feel_question column.
  • This is piped into the count() function, to indicate we want a count of every unique feel_increase_stress answer in the feel_response column, and gives names the new column of counts feel_increase_stress.
  • Finally, the arrange(desc(feel_increase_stres)) line arranges the values from biggest to smallest.

Your Turn!

Hint: Use the code chunk above to guide your solution.

  1. How many students feel more connected because of their social media use?
connected_count <- survey_data_tidy |>
  distinct(ID, feel_question, feel_response) |>
  filter(feel_question == "feel_connected") |>
  count(feel_response, name = "feel_connected") |>
  arrange(desc(feel_connected))

connected_count
  1. How many students feel distracted by their social media use?
distracted_count <- survey_data_tidy |>
  distinct(ID, feel_question, feel_response) |>
  filter(feel_question == "feel_distracted") |>
  count(feel_response, name = "feel_distracted") |>
  arrange(desc(feel_distracted))

distracted_count

Asking a Research Question

So far the questions we have been asking of our data have related to counts, which can be very helpful, but may not get to the bigger research question we want to ask. As part of the bigger research question, we might want to ask, “How does heavy usage of social media affect mental health compared to light usage?

There will be a few steps to answer this question, but let’s break them down and take them one at a time:

  1. Define heavy vs. light users
  2. Because the feel_response values are in a Likert scale, we need to convert them to numbers to allow us to get averages.
  3. Get the average score for each feel_question for both light and heavy users.
  4. Combine the groups
  5. Plot the comparison

Let’s get started!

Defining heavy vs. light users

In the hours_per_day question, survey respondents were given four possible answers:

  • Less than 2 hours
  • 2-4 hours
  • 4-6 hours
  • More than 6 hours

For the sake of our analysis, let’s say that respondents that selected “Less than 2 hours” and “2-4 hours” are light users, and those that selected “4-6 hours” and “More than 6 hours” are heavy users.

Let’s create a new column in our data to classify respondents as “light” or “heavy” users based on this criteria.

survey_data_tidy <- survey_data_tidy |>
  mutate(
    usage_group = case_when(
      hours_per_day %in% c("Less than 2 hours", "2-4 hours") ~ "Light",
      hours_per_day %in% c("4-6 hours", "More than 6 hours") ~ "Heavy",
    )
  )

Step-by-step explanation:

  • survey_data_tidy <- survey_data_tidy |> is telling R that we want to add/do something new to survey_data_tidy object, and save it back into the object.
  • This data is piped into the mutate() function, which is going to create a new column in our data called usage_group.
  • Everything after the = will be where we define what goes into this new column.
  • The case_when() function works in the following way: “if X condition is true, then do Y”.
  • hours_per_day %in% checks for values that are in the hours_per_day column, which are specified in the following brackets.
  • The c in c("Less than 2 hours", "2-4 hours") tells R we are looking for multiple values in the hours_per_day column, which in this case is “Less than 2 hours” and “2-4 hours”.
  • If hours_per_day equals either of these values, they will be give the value “Light” in the new column.
  • hours_per_day %in% c("4-6 hours", "More than 6 hours") ~ "Heavy" does this same process, but if these new conditions are met, they will be given the value “Heavy” in the new column.

Let’s take a look at survey_data_tidy to see this new column.

View(survey_data_tidy)

Convert Feel Responses to Numbers

The feel responses are in a Likert/textual format, and calculating the average of text isn’t something that is possible (the text must be counted or quantified, then calculated). To allow us to find the average, we’re going to create another column that applies numerical values to each Likert response.

Converting Likert responses to numbers.

survey_data_tidy <- survey_data_tidy |>
  mutate(
    feel_score = case_when(
      feel_response == "Strongly disagree" ~ 1,
      feel_response == "Disagree"          ~ 2,
      feel_response == "Neutral"           ~ 3,
      feel_response == "Agree"             ~ 4,
      feel_response == "Strongly agree"    ~ 5,
    )
  )

Step-by-step explanation:

  • survey_data_tidy <- survey_data_tidy |> is telling R that we want to add/do something new to survey_data_tidy object, and save it back into the object.
  • The data is piped into the mutate() function, which is going to create a new column.
  • The new column will be called feel_score, and will contain all the information after the =.
  • Much like the previous example, we are using case_when() to tell R that if X, then Y.
  • In this case feel_response == "Strongly disagree" ~ 1, we are saying, “if the feel_response column has a value that is”Strongly disagree”, we want to add the value “1” in the new column.
  • Repeat for each value in the Likert scale, and we’re ready to calculate!

Let’s take another look at survey_data_tidy.

View(survey_data_tidy)

We’re getting close! Let’s now calculate the average for light users. We’ll do this in two distinct steps.

Step 1: Isolate light users into their own grouping

light_users <- survey_data_tidy |>
  filter(usage_group == "Light")

Step-by-step explanation:

  • We’re creating a new R object called light_users, that will help us easily calculate values in this group.
  • After piping survey_data_tidy, we use the filter(user_group == "Light") to select only those rows in the user_group column that have the value “Light”.

We can view this new object:

View(light_users)

Step 2: Calculate the average of light users’ feel responses

light_avgs <- light_users |>
  group_by(feel_question) |>
  summarise(
    avg_feel = mean(feel_score),
    .groups = "drop") |>
  mutate(usage_group = "Light")

Step-by-step explanation:

  • We are creating a new object called light_avgs that will hold the averages for all feel responses for light users.
  • Pipe the data from the light_users object into group_by(feel_question), which tells R to calculate results separately for each feel question.
  • This is piped into the summarise() function, which sets up for the actual calculation we want to conduct.
  • avg_feel will create a new column with the values on the right side of the =.
  • mean(feel_score) calculates the average score for each feel response. This will give a table with one row per feel_question, and one column containing the average for each (avg_feel).
  • .groups = "drop" tells R to get rid of the grouping structure it uses to calculate the values. In plain language, “we’re done grouping, give me a regular table now”.
  • This is piped into mutate(user_group = "Light") to add a new column to the table called user_group, and every row will get the value “Light”. This step is helpful because when we combine the light and heavy users into one dataset for plotting, this will keep all the values sorted by their usage groups.

Let’s take a look at the new table we created!

light_avgs

Your Turn!

Now that you’ve seen how this works for the light users, see if you can do this for heavy users. This is a secret of using a coding language, where you don’t need to write every line from scratch, and you just need to understand code chunks enough to know what parts to change.

  1. Isolate heavy users into their own group, and save it as an object called heavy_users
heavy_users <- survey_data_tidy |>
  filter(usage_group == "Heavy")
  1. Calculate the average of heavy users’ feel responses, and save it as an object called heavy_avgs.
heavy_avgs <- heavy_users |>
  group_by(feel_question) |>
  summarise(
    avg_feel = mean(feel_score),
    .groups = "drop") |>
  mutate(usage_group = "Heavy")

Combine the Groups

We’re getting close! The next step we need to do is combine the light and heavy usage groups to make it very easy to plot. This can be done with a single line of code.

Combining the light and heavy user averages.

feel_avgs <- bind_rows(light_avgs, heavy_avgs)

Step-by-step explanation:

  • We’re creating a new R object called feel_avgs that will have the averages for all feel responses for both light and heavy users.
  • the bind_rows() function takes two or more data frames that have the **same columns*, and combines them by stacking them vertically.
  • lights_avgs and heavy_avgs are the two objects that have the averages, and this will effectively combine them.

Let’s take a look.

feel_avgs

Plotting the Results

Now that we have our data ready to go, we can now put it into a plot. Earlier in this session we used the barplot() function, which is great as a quick way to visualize data in bar charts. However, a much more powerful visualization package, that is part of the Tidyverse, is ggplot2.

This will not be a deep dive into ggplot2, but if you are interested in learning more, check out the session on Visualization with ggplot.

ggplot offers a number of different visualization types, and as it works in “layers”, you can keep adding more elements to your visualizations, making it an extremely powerful and precise tool. As mentioned, this won’t be a deep dive, but we’ll take a look at what visualizing our data in ggplot can do for our data.

Visualizing the questions “How does heavy usage of social media affect mental health compared to light usage?”

ggplot(feel_avgs, aes(x = feel_question, y = avg_feel, fill = usage_group)) +
  geom_col(position = "dodge")

Step-by-step explanation:

  • Every visualization with ggplot starts with the ggplot() command to tell R that we’re going to make a plot.
  • The first element to go in the brackets is the data we want to plot, which in this case is feel_avgs.
  • After a comma , we use the aes() function to describe the aesthetics of what the plot will look like. In this case, we specify that the x-axis will contain feel_question column in the table, and the y-axis will contain avg_feel, which is the averages. fill = usage_group indicates that we want each usage group to be filled with a different colour.
  • Now that we’ve specified the aesthetics, we insert a plus sign + to add additional elements.
  • geom_col() specifies the geom, or type of chart we want to make (there are many!), and position = "dodge" tells R that we want a separate column for light and heavy users, instead of a stacked column.

You’ll see that there’s now a plot in the viewer pane in RStudio (bottom right). Before we get to the data insights from the plot, it’s worth noting that there’s an issue with the chart. On the y-axis, we see numbers, which aren’t very good at communicating the Likert scale we’re visualizing, and the numbers end at 3 (they are chopped off because that’s where the averages stop). To communicate these findings more effectively, let’s update the y-axis to be more descriptive and easier to interpret.

Cleaning up the y-axis of our plot

ggplot(feel_avgs, aes(x = feel_question, y = avg_feel, fill = usage_group)) +
  geom_col(position = "dodge") +
  scale_y_continuous(
    breaks = 1:5,
    labels = c(
      "1 = Strongly disagree",
      "2 = Disagree",
      "3 = Neutral",
      "4 = Agree",
      "5 = Strongly agree"
    )
  ) +
  expand_limits(y = 5)

Step-by-step explanation:

Beginning where we left off at the first plot,

  • We and a plus sign + to tell R we want to add another layer.
  • scale_y_continuous( tells R we want to change how the y-axis looks, and everything in these brackets will add to this.
  • breaks = 1:5 tells R that we want to put tick marks at the values 1, 2, 3, 4, 5. 1:5 is shorthand for all the numbers between 1-5.
  • Don’t forget to separate the line with the comma ,!
  • labels = c("1 = Strongly disagree, ...) replaces the numbers on the y-axis with whatever textual labels are put in the quotation marks. Because we specified breaks = 1:5, it will accept five values. This can be expanded or reduced according to your plot.
  • There are two end brackets )) that need to be closed. One for scale_y_continuous( command, and one for labels = c(.
  • Add another plus sign + to add another layer.
  • expand_limits(y = 5) expands the chart to five tick marks, to make sure we can show the full scale.

Your Turn

The goal of this question is to test yourself in deciphering code that you might not be familiar with.

  1. Copy + paste this code in your RStudio editor and run it to see the results.
  2. Try to determine what each part of the code is doing.

Hint: Each functional element, or layer, of ggplot is separated by the plus sign +, and search engines are your friend!

avg_feel_plot <- ggplot(feel_avgs, aes(x = feel_question, y = avg_feel, fill = usage_group)) +
  geom_col(position = "dodge") +
  scale_y_continuous(
    breaks = 1:5,
    labels = c(
      "1 = Strongly disagree",
      "2 = Disagree",
      "3 = Neutral",
      "4 = Agree",
      "5 = Strongly agree"
    )
  ) +
  expand_limits(y = 5) +
  scale_x_discrete(
    labels = c(
      feel_connected      = "Makes me feel connected",
      feel_distracted     = "Makes me feel distracted",
      feel_improved_mood  = "Improves my mood",
      feel_increase_stress = "Increases my stress")) +
  scale_fill_discrete(
    name   = NULL,
    labels = c("Heavy users", "Light users")) +
  labs(
    x     = "Agree or disagree about social media and your mental health:",
    y     = "Average feeling response",
    title = "Social media and mental health across light and heavy users") +
  theme( axis.title.y = element_text(angle = 0, vjust = 0.5))

avg_feel_plot

Wrapping Up

Saving files

We haven’t generated as many files as we did in the previous session, but you’ll want to save two things:

  1. The R script for this session: file > save
  2. The survey_data_tidy.csv file that has new columns:
write_csv(survey_data_tidy, "survey_data_analyzed.csv")
  1. The final plot we created:
ggsave(filename = "avg_feel_plot.pdf", 
  plot = avg_feel_plot, 
  width = 8, 
  height = 6)

In addition to these files, you can imagine that you might want to save other snapshots of the data (ie. light_users, heavy_users, etc.).

Revisiting our file list:

  • avg_feel_plot.pdf
  • social-media-survey_ORIGINAL.csv
  • social-media-survey.csv
  • survey_analysis_script.R
  • survey_data_analyzed.csv
  • survey_data_tidy.csv
  • survey_data_wide.csv
  • survey_data.Rproj
  • survey_cleaning_script.R
  • survey-data_clean-cols_IDs.csv
  • survey-data_clean-cols_no-ID.csv

You can see that there’s some inconsistency with how files are named (hyphens - vs underscores _), and you might want to think about creating folders to start organizing things. While this is the end of the session, feel free to give this some thought and map out how you might want to structure these files in folders, and potentially rename then to better suit your use.

Finish

And that’s it for the workshop! We covered a lot of ground, but there is also a lot that we weren’t able to cover. The hope is that these sessions helped build some confidence in your ability to work with R, and that you use this as a jumping off point for your learning journey.

LS0tCnRpdGxlOiAiTWFraW5nIFNlbnNlIG9mIFN1cnZleSBEYXRhIgpwYWdldGl0bGU6ICJNYWtpbmcgU2Vuc2Ugb2YgU3VydmV5IERhdGEiCm91dHB1dDoKICBodG1sX2RvY3VtZW50OgogICAgY29kZV9mb2xkaW5nOiBzaG93ICMgYWxsb3dzIHRvZ2dsaW5nIG9mIHNob3dpbmcgYW5kIGhpZGluZyBjb2RlLiBSZW1vdmUgaWYgbm90IHVzaW5nIGNvZGUuCiAgICBjb2RlX2Rvd25sb2FkOiB0cnVlICMgYWxsb3dzIHRoZSB1c2VyIHRvIGRvd25sb2FkIHRoZSBzb3VyY2UgLlJtZCBmaWxlLiBSZW1vdmUgaWYgbm90IHVzaW5nIGNvZGUuCiAgICBpbmNsdWRlczoKICAgICAgYWZ0ZXJfYm9keTogZm9vdGVyLmh0bWwgIyBpbmNsdWRlIGEgY3VzdG9tIGZvb3Rlci4KICAgIHRvYzogdHJ1ZQogICAgdG9jX2RlcHRoOiAzCiAgICB0b2NfZmxvYXQ6CiAgICAgIGNvbGxhcHNlZDogZmFsc2UKICAgICAgc21vb3RoX3Njcm9sbDogZmFsc2UKLS0tCmBgYHtyIHNldHVwLCBpbmNsdWRlPUZBTFNFfQprbml0cjo6b3B0c19jaHVuayRzZXQobWVzc2FnZSA9IEZBTFNFLCB3YXJuaW5ncyA9IEZBTFNFKQpgYGAKCiMjIE1ha2luZyBTZW5zZSBvZiBTdXJ2ZXkgRGF0YQoKTm93IHRoYXQgd2UgaGF2ZSBjbGVhbmVkIHVwIG91ciBzdXJ2ZXkgZGF0YSwgd2UgY2FuIHN0YXJ0IGFza2luZyBxdWVzdGlvbnMgYWJvdXQgaXQhIEJ5IHRoZSBlbmQgb2YgdGhlIHByZXZpb3VzIHNlc3Npb24sIDxhIGhyZWY9IkJsb2NrOC0yX1NVUl9Db2xsZWN0LWFuZF9jbGVhbi5odG1sIj5Db2xsZWN0aW5nIGFuZCBDbGVhbmluZyBTdXJ2ZXkgRGF0YTwvYT4sIHdlIGdlbmVyYXRlZCB0d28gY2xlYW5lZCBkYXRhc2V0czoKCiogYHN1cnZleV9kYXRhX3RpZHkuY3N2YAoqIGBzdXJ2ZXktZGF0YV93aWRlLmNzdmAKCkxvbmcsIG9yICJ0aWR5IiBkYXRhIGFuZCB3aWRlIGRhdGEgZWFjaCBoYXZlIHRoZWlyIHByb3MgYW5kIGNvbnMsIGFuZCBpbiB0aGlzIHNlc3Npb24gd2UncmUgZ29pbmcgdG8gZXhwbG9yZSB0aGUgYmVuZWZpdHMgYW5kIGRyYXdiYWNrcyBvZiBlYWNoIGFzIHdlIGV4cGxvcmUgb3VyIGRhdGEuIEJ1dCBmaXJzdCwgbGV0J3MgdGFrZSBhIG1vbWVudCB0byBkbyBzb21lIHRoaW5raW5nOgoKVG8gYmVnaW4sIGxldCdzIG9wZW4gdXAgb3VyIGAuUlByb2pgIGZpbGUsIHNvIHRoYXQgb3VyIHdvcmtpbmcgZGlyZWN0b3J5IHdpbGwgYmUgY2FwdHVyZWQuIE5vdywgbGV0J3MgY3JlYXRlIGEgbmV3IFIgc2NyaXB0LCBhbmQgY2FsbCBpdCBgc3VydmV5X2FuYWx5c2lzX3NjcmlwdC5SYC4gCgojIyMgTG9hZCB0aGUgVGlkeXZlcnNlCgpOb3cgdGhhdCB3ZSd2ZSBzdGFydGVkIGEgbmV3IHNjcmlwdCwgd2UgbmVlZCB0byBsb2FkIHRoZSBsaWJyYXJpZXMgdGhhdCB3ZSdsbCBiZSB1c2luZywgd2hpY2ggaW4gdGhpcyBjYXNlIGlzIGp1c3QgdGhlIFRpZHl2ZXJzZS4KCmBgYHtyLCBldmFsPVRSVUUsIGluY2x1ZGU9RkFMU0V9CmxpYnJhcnkodGlkeXZlcnNlKQpgYGAKCmBgYHtyLCBldmFsPUZBTFNFfQpsaWJyYXJ5KHRpZHl2ZXJzZSkKYGBgCgojIyMgTG9hZCB0aGUgZGF0YQoKV2UgY2FuIG5vdyBsb2FkIHRoZSB0d28gZGF0YXNldHMgdGhhdCB3ZXJlIGdlbmVyYXRlZCBpbiB0aGUgcHJldmlvdXMgc2Vzc2lvbjoKCmBgYHtyLCBldmFsPVRSVUUsIGluY2x1ZGU9RkFMU0V9CnN1cnZleV9kYXRhX3RpZHkgPC0gcmVhZF9jc3YoImRhdGEvc3VydmV5LWNsZWFuaW5nLXdvcmtzaG9wL3N1cnZleV9kYXRhX3RpZHkuY3N2IikKYGBgCgpgYGB7ciwgZXZhbD1UUlVFLCBpbmNsdWRlPUZBTFNFfQpzdXJ2ZXlfZGF0YV93aWRlIDwtIHJlYWRfY3N2KCJkYXRhL3N1cnZleS1jbGVhbmluZy13b3Jrc2hvcC9zdXJ2ZXlfZGF0YV93aWRlLmNzdiIpCmBgYAoKYGBge3IsIGV2YWw9RkFMU0V9CnN1cnZleV9kYXRhX3RpZHkgPC0gcmVhZF9jc3YoInN1cnZleV9kYXRhX3RpZHkuY3N2IikKYGBgCgpgYGB7ciwgZXZhbD1GQUxTRX0Kc3VydmV5X2RhdGFfd2lkZSA8LSByZWFkX2Nzdigic3VydmV5X2RhdGFfd2lkZS5jc3YiKQpgYGAKCgo6OjpxdWVzdGlvbgoKMSkgVGFrZSBhIGxvb2sgYXQgYHN1cnZleV9kYXRhX3RpZHlgIGFuZCBgc3VydmV5LWRhdGFfd2lkZWAgdXNpbmcgdGhlIGBWaWV3KClgIGNvbW1hbmQuIApgYGB7ciwgY2xhc3Muc291cmNlID0gJ2ZvbGQtaGlkZScsIGV2YWwgPSBGQUxTRX0KVmlldyhzdXJ2ZXlfZGF0YV90aWR5KQpWaWV3KHN1cnZleV9kYXRhX3dpZGUpCmBgYAoyKSBJbiBncm91cHMsIHNwZW5kIGEgZmV3IG1pbnV0ZXMgdGFsa2luZyBhYm91dCBzb21lIHRoaW5ncyB5b3UnZCBsaWtlIHRvIGtub3cgYWJvdXQgdGhlIGRhdGEgKGllLiBncm91cHMgeW91J2QgbGlrZSB0byBjb3VudCwgcG9zc2libGUgcmVsYXRpb25zaGlwcywgZXRjLikuCgozKSBUYWtpbmcgYSBjbG9zZXIgbG9vayBhdCB0aGUgYHRpZHlgIGFuZCBgd2lkZWAgZGF0YXNldHMsIGNhbiB5b3UgdGhpbmsgb2YgcmVhc29ucyB3aHkgY2VydGFpbiBxdWVzdGlvbnMgbWlnaHQgYmUgYmV0dGVyIHdpdGggZWl0aGVyIG9uZSBvZiB0aGUgZGF0YXNldHM/Cgo6OjoKCiMjIEV4cGxvcmluZyBXaWRlIERhdGEKCldpZGUgZGF0YSBpcyBlYXNpZXIgZm9yIGh1bWFucyB0byByZWFkIGFuZCBpbnRlcnByZXQsIGFuZCBsZW5kcyBpdHNlbGYgd2VsbCB0byBhc2tpbmcgc2ltcGxlIHF1ZXN0aW9ucyBmcm9tIHNpbmdsZSB2YXJpYWJsZXMsIG9yIGNvbHVtbnMsIG9mIGRhdGEuIFRpZHkgZGF0YSBpcyBiZXR0ZXIgZm9yIGFza2luZyBtb3JlIGNvbXBsaWNhdGVkIHF1ZXN0aW9ucywgYXMgd2VsbCBhcyBhc2tpbmcgYWJvdXQgbXVsdGlwbGUgdmFyaWFibGVzIGFuZCBwb3NzaWJsZSByZWxhdGlvbnNoaXBzIGJldHdlZW4gdGhlbS4gTGV0J3MgZmlyc3Qgc3RhcnQgd2l0aCBzb21lIHNpbXBsZSBxdWVzdGlvbnMgd2UgY2FuIGFzayBmcm9tIHRoZSAqKndpZGUgdmVyc2lvbioqIG9mIG91ciBkYXRhLgoKQSBzaW1wbGUgZmlyc3Qgc3RlcCB3aGVuIGV4cGxvcmluZyBhIGRhdGFzZXQgaXMgZ2V0dGluZyBjb3VudHMgb2YgaW5kaXZpZHVhbCB2YXJpYWJsZXMuIFRvIGRvIHRoaXMsIHdlIGNhbiB1c2UgdGhlIGBjb3VudCgpYCBmdW5jdGlvbjoKCjo6OndhbGt0aHJvdWdoCgoqKldoYXQgZG9lcyB0aGUgc2FtcGxlIGJyZWFka2Rvd24gb2YgZ2VuZGVyPyoqCmBgYHtyLCBldmFsPUZBTFNFfQpzdXJ2ZXlfZGF0YV93aWRlIHw+CiAgY291bnQoZ2VuZGVyKQpgYGAKCioqU3RlcC1ieS1zdGVwIGV4cGxhbmF0aW9uOioqCgoxKSBXZSBzdGFydCB3aXRoIHRoZSBgc3VydmV5X2RhdGFfd2lkZWAgb2JqZWN0IGFuZCB1c2UgdGhlIHBpcGUsIGB8PmAsIHRvIGluZGljYXRlIHdlIHdhbnQgdG8gZG8gc29tZXRoaW5nIHdpdGggaXQuCjIpIFdlIHRoZW4gdXNlIHRoZSBgY291bnQoKWAgZnVuY3Rpb24gdG8gZ2l2ZSB1cyBhIHRvdGFsIHN1bSBvZiB3aGF0ZXZlciB2YXJpYWJsZS9jb2x1bW4gbmFtZSB3ZSBwdXQgaW5zaWRlIHRoZSBicmFja2V0cy4KOjo6CgoqKldoYXQgaXMgdGhlIHNhbXBsZSBicmVha2Rvd24gb2YgYWdlPyoqCmBgYHtyLCBldmFsPUZBTFNFfQpzdXJ2ZXlfZGF0YV93aWRlIHw+CiAgY291bnQoYWdlKQpgYGAKCkluIGFkZGl0aW9uIHRvIGxvb2tpbmcgYXQgc2luZ2xlIHZhcmlhYmxlcyB3aXRoIHRoZSBgY291bnQoKWAgZnVuY3Rpb24sIHdlIGNhbiBhbHNvIGdlbmVyYXRlIHF1aWNrIHN1bW1hcnkgdGFibGVzIGFjcm9zcyBtdWx0aXBsZSB2YXJpYWJsZXMgYnkgc2VwYXJhdGluZyB0aGVtIHdpdGggY29tbWFzIGAsYDoKCioqV2hhdCBpcyB0aGUgc2FtcGxlIGJyZWFrZG93biBvZiBhZ2UgYW5kIGdlbmRlcj8qKgpgYGB7ciwgZXZhbD1UUlVFfQpzdXJ2ZXlfZGF0YV93aWRlIHw+CiAgY291bnQoYWdlLCBnZW5kZXIpCmBgYAoKWW91J2xsIHNlZSB0aGUgbm90ZSB0aGF0IHRoZXJlIGFyZSAxNCBtb3JlIHJvd3MsIGFuZCB0aGF0IHlvdSBjYW4gdXNlIHRoZSBgcHJpbnQoKWAgZnVuY3Rpb24gdG8gdmlldyBtb3JlLiBZb3UgY2FuIGRvIHRoaXMgeW91IGNhbiBkbyB0aGlzIGJ5IHNwZWNpZml5aW5nIHRoZSBhbW91bnQgb2Ygcm93cyAoaW4gdGhpcyBjYXNlIHRoZXJlIGFyZSAyNCkKYGBge3IsIGV2YWw9RkFMU0V9CnN1cnZleV9kYXRhX3dpZGUgfD4KICBjb3VudChhZ2UsIGdlbmRlcikgfD4KICBwcmludChuID0gMjQpCmBgYAoKWW91IGNhbiBrZWVwIGFkZGluZyBhZGRpdGlvbmFsIHZhcmlhYmxlcyB0byB0aGVzZSwgbm90aW5nIHRoYXQgZWFjaCB2YXJpYWJsZSB3aWxsIGFkZCBhZGRpdGlvbmFsIHJvd3MuIElmIHlvdSBkb24ndCB3YW50IHRvIGxvb2sgYXQgdGhlIHZhbHVlcyBpbiB0aGUgUiBjb25zb2xlLCB5b3UgY2FuIHVzZSB0aGUgYFZpZXcoKWAgZnVuY3Rpb24gdG8gb3BlbiBpdCBpbiBhIHNlcGFyYXRlIHRhYi4KYGBge3IsIGV2YWw9RkFMU0V9CnN1cnZleV9kYXRhX3dpZGUgfD4KICBjb3VudChhZ2UsIGdlbmRlciwgeWVhcl9vZl9zdHVkeSkgfD4KICBWaWV3KCkKYGBgCgpJZiB0aGlzIGlzIHNvbWV0aGluZyB5b3Ugd2FudCB0byBjb21lIGJhY2sgdG8sIHlvdSBjYW4gc2F2ZSBpdCBpbnRvIGFuIFIgb2JqZWN0LgpgYGB7ciwgZXZhbD1GQUxTRX0KZGVtb2dyYXBoaWNzX2NvdW50IDwtIHN1cnZleV9kYXRhX3dpZGUgfD4KICBjb3VudChhZ2UsIGdlbmRlciwgeWVhcl9vZl9zdHVkeSkKClZpZXcoZGVtb2dyYXBoaWNzX2NvdW50KQpgYGAKCkEgZmluYWwgdGhpbmcgdGhhdCBjYW4gYmUgaGVscGZ1bCB3aXRoIHRoZSBgY291bnQoKWAgZnVuY3Rpb24gaXMgYWRkaW5nIGluIHRoZSBwcm9wb3J0aW9uIHRoYXQgZWFjaCBzdW0gcmVwcmVzZW50cyBpbiB0aGUgdG90YWwgc2FtcGxlLgoKOjo6d2Fsa3Rocm91Z2gKYGBge3IsIGV2YWw9RkFMU0V9CnN1cnZleV9kYXRhX3dpZGUgfD4KICBjb3VudChnZW5kZXIpIHw+CiAgbXV0YXRlKHByb3AgPSBuIC8gc3VtKG4pKQpgYGAKCioqU3RlcC1ieS1zdGVwIGV4cGxhbmF0aW9uOioqCgoxKSBTdGFydCBieSBwaXBpbmcgdGhlIGBzdXJ2ZXlfd2lkZWAgb2JqZWN0IHRvIHRoZSBgY291bnQoKWAgZnVuY3Rpb24gbGlrZSB3ZSd2ZSBiZWVuIGRvaW5nLgoyKSBBZGQgYW5vdGhlciBwaXBlIHRvIGluZGljYXRlIHdlIGFyZSBwYXNzaW5nIHRoYXQgY291bnQgdG8gYW5vdGhlciBmdW5jdGlvbi4KMykgYG11dGF0ZShwcm9wYCBjcmVhdGVzIGEgbmV3IGNvbHVtbiBjYWxsZWQgYHByb3BgLCB3aGljaCBzdGFuZHMgZm9yIHByb3BvcnRpb24gKHlvdSBjYW4gbmFtZSB0aGlzIHdoYXRldmVyIHlvdSB3YW50KS4KNCkgRXZlcnl0aGluZyBvbiB0aGUgcmlnaHQgc2lkZSBvZiB0aGUgYD1gIGlzIGFzc2lnbmVkIHRvIHRoZSBuZXcgYHByb3BgIGNvbHVtbi4gCjUpIGBuYCBpcyB0aGUgY291bnQgZm9yIGVhY2ggZ2VuZGVyIGNhdGVnb3J5LCBhbmQgYHN1bShuKWAgaXMgdGhlIHRvdGFsIG51bWJlciBvZiBldmVyeSByb3cvcGVyc29uIGluIHRoZSBzYW1wbGUuCjYpIGBuIC8gc3VtKG4pKWAgY2FsY3VsYXRlcyBgImNvdW50IG9mIGVhY2ggY2F0ZWdvcnkiIC8gInRvdGFsIGNvdW50ImAsIHdoaWNoIGdpdmVzIGEgcHJvcG9ydGlvbi4KCjo6OgoKIyMgWW91ciBUdXJuIQoKOjo6cXVlc3Rpb24KU2VlIGlmIHlvdSBjYW4gY3JlYXRlIHRoZSBmb2xsb3dpbmcgZnJvbSB0aGUgYHN1cnZleV9kYXRhX3dpZGVgIG9iamVjdDoKCjEpIEEgY291bnQgb2YgZWFjaCBgeWVhcl9vZl9zdHVkeWAgY2F0ZWdvcnkuCmBgYHtyLCBjbGFzcy5zb3VyY2UgPSAnZm9sZC1oaWRlJywgZXZhbD1GQUxTRX0Kc3VydmV5X2RhdGFfd2lkZSB8PgogIGNvdW50KHllYXJfb2Zfc3R1ZHkpCmBgYAoyKSBBIGNvdW50IG9mIGVhY2ggYHllYXJfb2Zfc3R1ZHlgIGFuZCBgaG91cnNfcGVyX2RheWAuCmBgYHtyLCBjbGFzcy5zb3VyY2UgPSAnZm9sZC1oaWRlJywgZXZhbD1GQUxTRX0Kc3VydmV5X2RhdGFfd2lkZSB8PgogIGNvdW50KHllYXJfb2Zfc3R1ZHksIGhvdXJzX3Blcl9kYXkpCmBgYAozKSBUaGUgY291bnQgb2YgYHllYXJfb2Zfc3R1ZHlgIGFuZCBpdHMgcHJvcG9ydGlvbi4KYGBge3IsIGNsYXNzLnNvdXJjZSA9ICdmb2xkLWhpZGUnLCBldmFsPUZBTFNFfQpzdXJ2ZXlfZGF0YV93aWRlIHw+CiAgY291bnQoeWVhcl9vZl9zdHVkeSkgfD4KICBtdXRhdGUocHJvcCA9IG4gLyBzdW0obikpCmBgYAo0KSBUaGUgY291bnQgb2YgYHllYXJfb2Zfc3R1ZHlgLCBgaG91cnNfcGVyX2RheWAsIGFuZCB0aGUgcHJvcG9ydGlvbgpgYGB7ciwgY2xhc3Muc291cmNlID0gJ2ZvbGQtaGlkZScsIGV2YWw9RkFMU0V9CnN1cnZleV9kYXRhX3dpZGUgfD4KICBjb3VudCh5ZWFyX29mX3N0dWR5LCBob3Vyc19wZXJfZGF5KSB8PgogIG11dGF0ZShwcm9wID0gbiAvIHN1bShuKSkKYGBgCjo6OgoKIyMgU2ltcGxlIEJhcnBsb3RzIGFzIEV4cGxvcmF0aW9uCgpJbiBhZGRpdGlvbiB0byBjcmVhdGluZyBzdW1tYXJ5IHRhYmxlcyB0byBleHBsb3JlIGRhdGEsIGNyZWF0aW5nIHNpbXBsZSBwbG90cyBjYW4gYmUgYW5vdGhlciB3YXkgdG8gZXhwbG9yZSB5b3VyIGRhdGEuCgpPbmUgd2F5IHRvIGRvIHRoaXMsIGlzIHVzaW5nIHRoZSBiYXNlIFIgYGJhcnBsb3QoKWAgZnVuY3Rpb24uIFRoaXMgY2FuIGJlIGRvbmUgaW4gYSBmZXcgc3RlcHMuCgoKSW4gdGhpcyBleGFtcGxlLCB3ZSdyZSBnb2luZyB0byBwbG90IHRoZSBjYXRlZ29yaWVzIG9mIHRoZSBgaG91cnNfcGVyX2RheWAgY29sdW1ucy4KCkZpcnN0LCBsZXQncyBsb29rIGF0IHRoZSBgJGAgb3BlcmF0b3IuIFdoZW4gcGxhY2VkIGFmdGVyIGEgZGF0YSBvYmplY3QgaW4gUiwgaXQgYWxsb3dzIHlvdSB0byBpc29sYXRlIHNwZWNpZmllZCBjb2x1bW5zLgoKYGBge3IsIGV2YWw9RkFMU0V9CnN1cnZleV9kYXRhX3dpZGUkaG91cnNfcGVyX2RheQpgYGAKClRoaXMgd2lsbCBzaG93IGFsbCB0aGUgdmFsdWVzIGluIHRoYXQgY29sdW1uIGluIHRoZSBSIGNvbnNvbGUuIEdpdmUgaXQgYSB0cnkgd2l0aCBvdGhlciBjb2x1bW5zIQoKTmV4dCwgd2UgY2FuIGFsc28gdXNlIHRoZSBgdGFibGVgIGZ1bmN0aW9uIGhlcmUsIHRvIGdlbmVyYXRlIGEgcXVpY2sgc3VtbWFyeSB0YWJsZSBvZiB0aGF0IGNvbHVtbi4KCmBgYHtyLCBldmFsPUZBTFNFfQp0YWJsZShzdXJ2ZXlfZGF0YV93aWRlJGhvdXJzX3Blcl9kYXkpCmBgYAoKR2l2ZSB0aGlzIGEgdHJ5IHdpdGggc29tZSBvdGhlciBjb2x1bW5zIQoKTm93IHdlJ3JlIGdvaW5nIHRvIHNhdmUgdGhpcyB0YWJsZSBhcyBhbiBvYmplY3QgY2FsbGVkIGBob3Vyc190YWJsZWAuCgpgYGB7ciwgZXZhbD1UUlVFfQpob3Vyc190YWJsZSA8LSB0YWJsZShzdXJ2ZXlfZGF0YV93aWRlJGhvdXJzX3Blcl9kYXkpCmBgYAoKRmluYWxseSwgd2UgY2FuIGZlZWQgdGhlIGBob3Vyc190YWJsZWAgb2JqZWN0IGludG8gdGhlIGBiYXJwbG90KClgIGZ1bmN0aW9uIHRvIGdldCBvdXIgcGxvdC4KCmBgYHtyLCBldmFsPVRSVUV9CmJhcnBsb3QoaG91cnNfdGFibGUpCmBgYAoKWW91IG1pZ2h0IG5vdGljZSB0aGF0IHRoZSBiYXJzIGFyZW4ndCBpbiBhbnkgcGFydGljdWxhciBvcmRlciwgd2hpY2ggY2FuIGJlIGEgYml0IGFubm95aW5nLiBMZXQncyBjbGVhbiB0aGlzIHVwIGJ5IGNyZWF0aW5nIGFkZGluZyBhbm90aGVyIHN0ZXAuIAoKYGBge3IsIGV2YWw9VFJVRX0KaG91cnNfc29ydGVkIDwtIHNvcnQoaG91cnNfdGFibGUsIGRlY3JlYXNpbmcgPSBUUlVFKQpgYGAKCmBgYHtyLCBldmFsPVRSVUV9CmJhcnBsb3QoaG91cnNfc29ydGVkKQpgYGAKClRoZSBgc29ydGAgZnVuY3Rpb24gaXMgdGVsbGluZyBSIHRoYXQgd2Ugd2FudCB0byByZW9yZGVyIHRoZSB2YWx1ZXMgaW4gYGhvdXJzX3RhYmxlYCwgYW5kIHRoZSBgZGVjcmVhc2luZyA9IFRSVUVgIGFsbG93cyB1cyB0byBzZWUgdGhlIHZhbHVlcyBmcm9tIGJpZ2dlc3QgdG8gc21hbGxlc3QuIFlvdSBjYW4gY2hhbmdlIHRoaXMgdG8gYGRlY3JlYXNpbmcgPSBGQUxTRWAgdG8gc2VlIHNtYWxsZXN0IHRvIGJpZ2dlc3QuCgpXaGVuIGV4cGxvcmluZyBkYXRhLCBpdCdzIG5vdCBhbHdheXMgbmVjZXNzYXJ5IHRvIGFkZCBsYWJlbHMgdG8gb3VyIHBsb3RzLiBIb3dldmVyLCB0aGlzIGNoYXJ0IG1pZ2h0IGJlIHNvbWV0aGluZyB5b3Ugd2FudCB0byBjb21tdW5pY2F0ZSwgc28gbGV0J3MgYWRkIHNvbWUgbGFiZWxzIQoKYGBge3IsIGV2YWw9VFJVRX0KYmFycGxvdChob3Vyc19zb3J0ZWQsCiAgICAgICAgbWFpbiA9ICJIb3VycyBwZXIgZGF5IG9uIHNvY2lhbCBtZWRpYSIsCiAgICAgICAgeGxhYiA9ICJIb3VycyBwZXIgZGF5IiwKICAgICAgICB5bGFiID0gIk51bWJlciBvZiBzdHVkZW50cyIpCmBgYAoKV2l0aCB0aGUgYGJhcnBsb3QoKWAgZnVuY3Rpb246CgoqIGBtYWluYCBpcyB0aGUgdGl0bGUgb2YgdGhlIGNoYXJ0LgoqIGB4bGFiYCBpcyB0aGUgbGFiZWwgb24gdGhlIHggKGhvcml6b250YWwpIGF4aXMuCiogYHlsYWRgIGlzIHRoZSBsYWJlbCBvbiB0aGUgeSAodmVydGljYWwpIGF4aXMuCgojIyBZb3VyIFR1cm4hCgo6OjpxdWVzdGlvbgoKMSkgTWFrZSBhIGJhcnBsb3QgZm9yIHRoZSBgZmVlbF9pbmNyZWFzZV9zdHJlc3NgIHZhcmlhYmxlLCB0aGF0IG9yZGVycyB0aGUgYmFycyBmcm9tIGJpZ2dlc3QgdG8gc21hbGxlc3QsIGFuZCBoYXMgbGFiZWxzIGZvciB0aGUgdGl0bGUsIGFuZCBib3RoIGF4ZXMuCgpIaW50OiBicmVhayB0aGlzIGludG8gMyBzdGVwczoKCiogQ3JlYXRlIGFuIG9iamVjdCB0aGF0IGNyZWF0ZXMgYSB0YWJsZSBvZiB0aGUgYGZlZWxfaW5jcmVhc2Vfc3RyZXNzYCBjb2x1bW4KKiBDcmVhdGUgYW5vdGhlciBvYmplY3QgdGhhdCBzb3J0cyB0aGUgdGFibGUgaW4gZGVjcmVhc2luZyBvcmRlcgoqIENyZWF0ZSBhIGJhcnBsb3QgYW5kIGFkZCAzIGxheWVycyBvZiBsYWJlbHMuCgpgYGB7ciwgY2xhc3Muc291cmNlID0gJ2ZvbGQtaGlkZScsIGV2YWw9RkFMU0V9CnN0cmVzc19jb3VudCA8LSB0YWJsZShzdXJ2ZXlfZGF0YV93aWRlJGZlZWxfaW5jcmVhc2Vfc3RyZXNzKQoKc3RyZXNzX3NvcnRlZCA8LSBzb3J0KHN0cmVzc19jb3VudCwgZGVjcmVhc2luZyA9IFRSVUUpCgpiYXJwbG90KHN0cmVzc19zb3J0ZWQsCiAgICAgICAgbWFpbiA9ICJTb2NpYWwgbWVkaWEgbWFrZXMgbWUgZmVlbCBhbnhpb3VzIG9yIHN0cmVzc2VkIiwKICAgICAgICB4bGFiID0gIkZlZWxpbmcgYW54aW91cyBvciBzdHJlc3NlZCIsCiAgICAgICAgeWxhYiA9ICJOdW1iZXIgb2Ygc3R1ZGVudHMiKQpgYGAKCjIpIFRyeSB0aGlzIHdpdGggYW5vdGhlciBjb2x1bW4hCgo6OjoKCgojIyBNb3ZpbmcgdG8gVGlkeSBEYXRhCgpBcyB3ZSd2ZSBzZWVuLCB3b3JraW5nIHdpdGggd2lkZSBkYXRhIGNhbiBiZSBoZWxwZnVsIGZvciBnZW5lcmF0aW5nIHF1aWNrIHN1bW1hcmllcyBhbmQgY291bnRzLiBCdXQgd2hhdCBpZiB3ZSB3YW50IHRvIGFzayBzb21ldGhpbmcgdGhhdCdzIGEgYml0IG1vcmUgY29tcGxpY2F0ZWQ/Cgo6OjpxdWVzdGlvbgoKSW4gZ3JvdXBzLCB0YWtlIGEgbG9vayBhdCB0aGUgZGF0YSBhbmQgdGFsayBhYm91dCBob3cgeW91IG1pZ2h0IGdvIGFib3V0IGFuc3dlcmluZyB0aGUgcXVlc3Rpb24sICoqd2hpY2ggcGxhdGZvcm1zIGFyZSBtb3N0IHVzZWQ/KioKCjo6OgoKVGhlcmUgYXJlIGNlcnRhaW5seSB3YXlzIHRvIGRvIHRoaXMgd2l0aCB0aGUgd2lkZSBkYXRhLiBPbmUgd2F5IHdvdWxkIGJlIHRvIHVzZSB0aGUgYGNvdW50KClgIGZ1bmN0aW9uIGZvciBlYWNoIGluZGl2aWR1YWwgY29sdW1uLiBIb3dldmVyLCB0aGlzIGlzIGEgYml0IGNsdW5reSwgYW5kIHRoZW4geW91IHdvdWxkIGhhdmUgdG8gdGhlbiBkZWFsIHdpdGggbWVyZ2luZyB0aGUgcmVzcG9uc2VzIGludG8gYSB0YWJsZSAobm90IGltcG9zc2libGUsIGJ1dCBpdCdzIG1vcmUgd29yaykuIFRoZXJlIGlzIGEgd2F5IHRvIGRvIHRoaXMgaXMgYSBzaW5nbGUgY29kZSBjaHVuaywgYnV0IGl0IGJlY29tZXMgb3Zlcmx5IGxvbmcgYW5kIGNvbXBsaWNhdGVkOgoKYGBge3IsIGV2YWw9RkFMU0V9CnBsYXRmb3JtX2NvdW50c193aWRlIDwtIHN1cnZleV93aWRlIHw+CiAgc3VtbWFyaXNlKAogICAgYWNyb3NzKAogICAgICBjKGBMaW5rZWRJbmAsIEluc3RhZ3JhbSwgUmVkZGl0LCBGYWNlYm9vaywKICAgICAgICBgWCAoVHdpdHRlcilgLCBUaWtUb2ssIFNuYXBjaGF0KSwKICAgICAgfiBzdW0oLngpKSkgfD4KICBwaXZvdF9sb25nZXIoCiAgICBjb2xzID0gZXZlcnl0aGluZygpLAogICAgbmFtZXNfdG8gPSAicGxhdGZvcm0iLAogICAgdmFsdWVzX3RvID0gIm5fdXNlcnMiKSB8PgogIGFycmFuZ2UoZGVzYyhuX3VzZXJzKSkKCnBsYXRmb3JtX2NvdW50c193aWRlCmBgYAoKV2UgZG9uJ3QgbmVlZCB0byB1bmRlcnN0YW5kIHRoaXMgY29kZSBjaHVuaywgaXQgaXMganVzdCBhbiBleGFtcGxlIG9mIHdoYXQgdGhlIGNvZGUgbG9va3MgbGlrZS4gSG93ZXZlciwgaWYgd2Ugc2hpZnQgdG8gKip0aWR5KiogZGF0YSwgdGhlIGNvZGUgYmVjb21lcyBtdWNoIHNob3J0ZXIgYW5kIHNpbXBsZXIuCgpGaXJzdCwgbGV0J3Mgc3RhcnQgYnkgdGFraW5nIGFub3RoZXIgbG9vayBhdCB0aGUgdGlkeSBkYXRhLgoKYGBge3IsIGV2YWw9RkFMU0V9ClZpZXcoc3VydmV5X2RhdGFfdGlkeSkKYGBgCgpZb3UgY2FuIHNlZSB0aGF0IHRoZXJlIGlzIHRoZSBzaW5nbGUgYHBsYXRmb3Jtc2AgY29sdW1uIHRoYXQgd2UgYXJlIGFibGUgdG8gcXVlcnkuCgpOb3csIGxldCdzIGRvIGEgY291bnQgb2YgdGhlIGRpZmZlcmVudCBjYXRlZ29yaWVzIGluIHRoaXMgY29sdW1uLgoKYGBge3IsIGV2YWw9VFJVRX0Kc3VydmV5X2RhdGFfdGlkeSB8PgogIGNvdW50KHBsYXRmb3JtcywgbmFtZSA9ICJuX3N0dWRlbnRzIikKYGBgCgpJbiB0aGlzIGNvZGUsIHdlIGFyZSB1c2luZyB0aGUgYGNvdW50KClgIGZ1bmN0aW9uIGxpa2Ugd2UganVzdCBkaWQsIGFuZCB0aGUgYG5hbWUgPSAibl9zdHVkZW50cyJgIGNyZWF0ZXMgYSBuZXcgY29sdW1uIG5hbWUgZm9yIHRoZSBudW1iZXIgZm9yIGVhY2ggY29sdW1uLiBgIm5fc3R1ZGVudHMiYCB3YXMgdXNlZCB0byBzaG93IHRoaXMgaXMgdGhlIG51bWJlciBvZiBzdHVkZW50cywgYnV0IHlvdSBjYW4gcHV0IGFueXRoaW5nIHlvdSB3YW50IGhlcmUsIG9yIGxlYXZlIGl0IGJsYW5rIGlmIHByZWZlcnJlZC4KCllvdSBjYW4gc2VlIHRoYXQgdGhpcyBjb2RlIGlzIHZlcnkgc2ltcGxlLCBidXQgdGhlcmUgaXMgYSBiaWcgcHJvYmxlbSB3aXRoIHRoZSBudW1iZXJzISBCZWNhdXNlIG91ciB0aWR5IGRhdGEgYWRkcyBhZGRpdGlvbmFsIHJvd3MgdG8gZGF0YSwgd2UgYXJlIHNlZWluZyBudW1iZXJzIGluIHRoZSB0aG91c2FuZHMgd2hlbiBvdXIgc2FtcGxlIHNpemUgaXMgb25seSAzMDAuIEhvd2V2ZXIsIHdoZW4gd2UgY2xlYW5lZCB0aGUgZGF0YSwgd2UgY3JlYXRlZCBhIHVuaXF1ZSBJRCBmb3IgZWFjaCBlbnRyeS4gVGhpcyB3YXMgdG8gaGVscCB3aXRoIHJlLWlkZW50aWZ5aW5nIHJlc3BvbnNlcyBpZiBuZWVkZWQsIGJ1dCBpdCBjYW4gYWxzbyBoZWxwIHVzIHdpdGggZWxpbWluYXRpbmcgcmVkdW5kYW50IHJvd3MuCgpgYGB7ciwgZXZhbD1UUlVFfQpzdXJ2ZXlfZGF0YV90aWR5IHw+CiAgZGlzdGluY3QoSUQsIHBsYXRmb3JtcykgfD4KICBjb3VudChwbGF0Zm9ybXMsIG5hbWUgPSAibl9zdHVkZW50cyIpCmBgYAoKVGhlIGBkaXN0aW5jdCgpYCBmdW5jdGlvbiBpcyB0ZWxsaW5nIFIgdGhhdCB3ZSBvbmx5IHdhbnQgdG8ga2VlcCB1bmlxdWUgY29tYmluYXRpb25zIG9mIGVhY2ggc3R1ZGVudCBgSURgIGFuZCBgcGxhdGZvcm1gLCB3aGljaCBlbGltaW5hdGVzIGFueSBvZiB0aGUgcmVkdW5kYW5jeSBvZiB0aGUgdGlkeSBkYXRhLiBBcyB5b3UgY2FuIHNlZSwgdGhlIG51bWJlcnMgYXJlIG5vdyBhY2N1cmF0ZSB0byBvdXIgc2FtcGxlLgoKQXMgd2l0aCBiZWZvcmUsIGl0IGNhbiBiZSBuaWNlIHRvIGFycmFuZ2UgdGhlIG51bWJlcnMgZnJvbSBiaWdnZXN0IHRvIHNtYWxsZXN0LgoKYGBge3IsIGV2YWw9VFJVRX0Kc3VydmV5X2RhdGFfdGlkeSB8PgogIGRpc3RpbmN0KElELCBwbGF0Zm9ybXMpIHw+CiAgY291bnQocGxhdGZvcm1zLCBuYW1lID0gIm5fc3R1ZGVudHMiKSB8PgogIGFycmFuZ2UoZGVzYyhuX3N0dWRlbnRzKSkKYGBgCgpJbiB0aGlzIGNhc2UsIHRoZSBgYXJyYW5nZSgpYCBmdW5jdGlvbiB0ZWxscyBSIHRoYXQgd2Ugd2FudCB0byBjaGFuZ2UgdGhlIG9yZGVyIG9mIHNvbWV0aGluZywgYW5kIGBkZXNjKG5fc3R1ZGVudHMpYCBzcGVjaWZpZXMgdGhhdCB3ZSB3YW50IHRoZSBgbl9zdHVkZW50c2AgY29sdW1uLCB3aGljaCBpcyB0aGUgdmFsdWVzIGZvciBlYWNoIHBsYXRmb3JtLCB0byBiZSBsaXN0ZWQgaW4gZGVzY2VuZGluZyBvcmRlciAoYmlnZ2VzdCB0byBzbWFsbGVzdCkuCgpGaW5hbGx5LCBvbmNlIHdlJ3JlIGhhcHB5IHdpdGggdGhlIG91dHB1dCBvZiB0aGlzIGNvZGUgY2h1bmssIHdlIGNhbiBzYXZlIGl0IGFzIGFuIFIgb2JqZWN0IHRvIHJldmlzaXQuIFdlJ2xsIGNhbGwgdGhlIG9iamVjdCBgcGxhdGZvcm1fY291bnRgLCBhbmQgbm93IHdlIGNhbiBlYXNpbHkgdmlldyBhbmQgd29yayB3aXRoIGl0IGdvaW5nIGZvcndhcmQuCgpgYGB7ciwgZXZhbD1UUlVFfQpwbGF0Zm9ybV9jb3VudCA8LSBzdXJ2ZXlfZGF0YV90aWR5IHw+CiAgZGlzdGluY3QoSUQsIHBsYXRmb3JtcykgfD4KICBjb3VudChwbGF0Zm9ybXMsIG5hbWUgPSAibl9zdHVkZW50cyIpIHw+CiAgYXJyYW5nZShkZXNjKG5fc3R1ZGVudHMpKQpgYGAKCiMjIEdvaW5nIERlZXBlciB3aXRoIFRpZHkgRGF0YQoKTGV0J3Mga2VlcCBkaWdnaW5nIGludG8gdGhlIHRpZHkgZGF0YSwgYW5kIHN0YXJ0IGFza2luZyBxdWVzdGlvbnMgdGhhdCBhcmUgcmVsYXRlZCB0byBvdXIgYmlnZ2VyIHJlc2VhcmNoIHF1ZXN0aW9uLCAqKiJIb3cgZG9lcyBzb2NpYWwgbWVkaWEgdXNhZ2UgaW5mbHVlbmNlIHRoZSBtZW50YWwgaGVhbHRoIG9mIHVuaXZlcnNpdHkgc3R1ZGVudHM/KioKCiMjIyAqKkNvbXBhcmlzb24gT3BlcmF0b3JzKioKCkNvbXBhcmlzb24gb3BlcmF0b3JzIGFsbG93IHVzIHRvIGxvb2sgZm9yIHNwZWNpZmljIGNyaXRlcmlhIGluIG91ciBkYXRhc2V0LiBUaGVzZSByZXR1cm4gYFRSVUVgIG9yIGBGQUxTRWAgYmFzZWQgb24gd2hldGhlciB0aGUgY29uZGl0aW9uIGlzIG1ldC4KCnwgT3BlcmF0b3IgfCBNZWFuaW5nICAgICAgICAgICAgICAgICAgfCBFeGFtcGxlICB8IFJlc3VsdCB8CnwtLS0tLS0tLS0tfC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tfC0tLS0tLS0tLS18LS0tLS0tLS18CnwgYD09YCAgICAgfCBFcXVhbCB0byAgICAgICAgICAgICAgICAgfCBgNSA9PSA1YCB8IGBUUlVFYCB8CnwgYCE9YCAgICAgfCBOb3QgZXF1YWwgdG8gICAgICAgICAgICAgfCBgNSAhPSAzYCB8IGBUUlVFYCB8CnwgYDxgICAgICAgfCBMZXNzIHRoYW4gICAgICAgICAgICAgICAgfCBgMyA8IDVgICB8IGBUUlVFYCB8CnwgYD5gICAgICAgfCBHcmVhdGVyIHRoYW4gICAgICAgICAgICAgfCBgNSA+IDNgICB8IGBUUlVFYCB8CnwgYDw9YCAgICAgfCBMZXNzIHRoYW4gb3IgZXF1YWwgdG8gICAgfCBgMyA8PSAzYCB8IGBUUlVFYCB8CnwgYD49YCAgICAgfCBHcmVhdGVyIHRoYW4gb3IgZXF1YWwgdG8gfCBgNSA+PSAzYCB8IGBUUlVFYCB8CgpPbmNlIHdlIHVuZGVyc3RhbmQgdGhlc2UgYmFzaWMgY29tcGFyaXNvbiBvcGVyYXRvcnMsIHdlIGNhbiBjb21iaW5lIHRoZW0gdXNpbmcgbG9naWNhbCBvcGVyYXRvcnMgdG8gY3JlYXRlIG1vcmUgY29tcGxleCBmaWx0ZXJpbmcgY29uZGl0aW9ucy4KCiMjIyAqKkxvZ2ljYWwgT3BlcmF0b3JzKioKCkxvZ2ljYWwgb3BlcmF0b3JzIGFsbG93IHVzIHRvIGZpbHRlciBkYXRhIGJhc2VkIG9uIG11bHRpcGxlIGNvbmRpdGlvbnMuCgp8IE9wZXJhdG9yIHwgTWVhbmluZyAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwgRXhhbXBsZSAgICAgICAgICAgICB8IFJlc3VsdCAgfAp8LS0tLS0tLS0tLXwtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS18LS0tLS0tLS0tLS0tLS18LS0tLS0tLS0tLS0tLXwKfCBgJmAgICAgICB8IExvZ2ljYWwgQU5EIChCb3RoIGNvbmRpdGlvbnMgbXVzdCBiZSBUUlVFKSAgICAgICB8IGAoNSA+IDMpICYgKDQgPCA2KWAgfCBgVFJVRWAgIHwKfCBgfGAgICAgICB8IExvZ2ljYWwgT1IgKEF0IGxlYXN0IG9uZSBjb25kaXRpb24gbXVzdCBiZSBUUlVFKSB8IGAoNSA+IDMpIHwgKDQgPiA2KWAgfCBgVFJVRWAgIHwKfCBgIWAgICAgICB8IExvZ2ljYWwgTk9UIChSZXZlcnNlcyBUUlVFL0ZBTFNFKSAgICAgICAgICAgICAgICB8IGAhKDUgPiAzKWAgICAgICAgICAgfCBgRkFMU0VgIHwKCgojIyMgQXNraW5nIFF1ZXN0aW9ucyB3aXRoIEZpbHRlcmluZwoKOjo6bm90ZQoKVGhlIGBmaWx0ZXIoKWAgZnVuY3Rpb24gcmV0dXJucyBvbmx5IHRoZSByb3dzIHRoYXQgbWVldCBhIHNwZWNpZmllZCBjb25kaXRpb24uCgo6OjoKCkZyb20gdGhlIGBmZWVsLXF1ZXN0aW9uYCBjb2x1bW4gaW4gb3VyIHRpZHkgZGF0YSwgdGhlc2UgdmFsdWVzIGluZGljYXRpbmcgd2hldGhlciBzb2NpYWwgbWVkaWEgbWFrZXMgc3R1ZGVudHMgZmVlbCBjb25uZWN0ZWQgdG8gb3RoZXJzIChgZmVlbF9jb25uZWN0ZWRgKSwgYW54aW91cyBvciBzdHJlc3NlZCAoYGZlZWxfaW5jcmVhc2Vfc3RyZXNzYCksIGRpc3RyYWN0cyB0aGVtIGZyb20gYWNhZGVtaWMgd29yayAoYGZlZWxfZGlzdHJhY3RlZGApLCBvciBpbXByb3ZlcyB0aGVpciBtb29kIChgZmVlbF9pbXByb3ZlZF9tb29kYCkuIAoKQXMgcGFydCBvZiB0aGUgYmlnZ2VyIHJlc2VhcmNoIHF1ZXN0aW9uLCAiSG93IGRvZXMgc29jaWFsIG1lZGlhIHVzYWdlIGluZmx1ZW5jZSB0aGUgbWVudGFsIGhlYWx0aCBvZiB1bml2ZXJzaXR5IHN0dWRlbnRzPyIsIHdlIGNhbiB1c2UgdGhpcyB2YXJpYWJsZSB0byBzdGFydCBhc2tpbmcgdGhlIHF1ZXN0aW9uLCAqKiJob3cgbWFueSBzdHVkZW50cyBmZWVsIFggYmVjYXVzZSBvZiB0aGVpciBzb2NpYWwgbWVkaWEgdXNlPyoqCgo6Ojp3YWxrdGhyb3VnaAoKTGV0J3MgZmlyc3Qgc3RhcnQgYnkgYXNraW5nLCAiaG93IG1hbnkgc3R1ZGVudHMgZmVlbCAqKmFueGlvdXMgb3Igc3RyZXNzZWQqKiBiZWNhdXNlIG9mIHNvY2lhbCBtZWRpYT8KCmBgYHtyLCBldmFsPVRSVUV9CnN0cmVzc2VkX2NvdW50IDwtIHN1cnZleV9kYXRhX3RpZHkgfD4KICBkaXN0aW5jdChJRCwgZmVlbF9xdWVzdGlvbiwgZmVlbF9yZXNwb25zZSkgfD4KICBmaWx0ZXIoZmVlbF9xdWVzdGlvbiA9PSAiZmVlbF9pbmNyZWFzZV9zdHJlc3MiKSB8PgogIGNvdW50KGZlZWxfcmVzcG9uc2UsIG5hbWUgPSAiZmVlbF9pbmNyZWFzZV9zdHJlc3MiKSB8PgogIGFycmFuZ2UoZGVzYyhmZWVsX2luY3JlYXNlX3N0cmVzcykpCgpzdHJlc3NlZF9jb3VudApgYGAKCioqU3RlcC1ieS1zdGVwIGV4cGxhbmF0aW9uOioqCgoqIFdlIHN0YXJ0IGJ5IGNyZWF0aW5nIGEgbmV3IFIgb2JqZWN0IHRoYXQgd2lsbCBzYXZlIHRoaXMgaW5mb3JtYXRpb24sIGNhbGxlZCBgc3RyZXNzZWRfY291bnRgLgoqIFdlIHRoZW4gcGlwZSB0aGUgYHN1cnZleV9kYXRhX3RpZHlgIG9iamVjdCB0byBpbmRpY2F0ZSB3ZSB3aWxsIG1hbmlwdWxhdGUgaXQgaW4gc29tZSB3YXkuCiogTmV4dCwgYGRpc3RpbmN0KElELCBmZWVsX3F1ZXN0aW9uLCBmZWVsX3Jlc3BvbnNlKWAgdGVsbHMgUiB0byBvbmx5IGtlZXAgdW5pcXVlIGNvbWJpbmF0aW9ucyBvZiBgSURgLCBgZmVlbC1xdWVzdGlvbmAsIGFuZCBgZmVlbF9yZXNwb25zZWAuCiogVGhpcyBpcyB0aGVuIHBpcGVkIHRocm91Z2ggdG8gdGhlIGBmaWx0ZXIoKWAgY29tbWFuZCwgYW5kIGBmZWVsX3F1ZXN0aW9uID09IGZlZWxfaW5jcmVhc2Vfc3RyZXNzYCBsb29rcyBmb3Igb25seSBmb3IgdGhlIHN0cmVzc2VkIHZhbHVlcyBpbiB0aGUgYGZlZWxfcXVlc3Rpb25gIGNvbHVtbi4KKiBUaGlzIGlzIHBpcGVkIGludG8gdGhlIGBjb3VudCgpYCBmdW5jdGlvbiwgdG8gaW5kaWNhdGUgd2Ugd2FudCBhIGNvdW50IG9mIGV2ZXJ5IHVuaXF1ZSBgZmVlbF9pbmNyZWFzZV9zdHJlc3NgIGFuc3dlciBpbiB0aGUgYGZlZWxfcmVzcG9uc2VgIGNvbHVtbiwgYW5kIGdpdmVzIG5hbWVzIHRoZSBuZXcgY29sdW1uIG9mIGNvdW50cyBgZmVlbF9pbmNyZWFzZV9zdHJlc3NgLgoqIEZpbmFsbHksIHRoZSBgYXJyYW5nZShkZXNjKGZlZWxfaW5jcmVhc2Vfc3RyZXMpKWAgbGluZSBhcnJhbmdlcyB0aGUgdmFsdWVzIGZyb20gYmlnZ2VzdCB0byBzbWFsbGVzdC4KCjo6OgoKIyMgWW91ciBUdXJuIQoKOjo6cXVlc3Rpb24KCkhpbnQ6IFVzZSB0aGUgY29kZSBjaHVuayBhYm92ZSB0byBndWlkZSB5b3VyIHNvbHV0aW9uLgoKMSkgSG93IG1hbnkgc3R1ZGVudHMgZmVlbCAqbW9yZSBjb25uZWN0ZWQqIGJlY2F1c2Ugb2YgdGhlaXIgc29jaWFsIG1lZGlhIHVzZT8KCmBgYHtyLCBjbGFzcy5zb3VyY2UgPSAnZm9sZC1oaWRlJywgZXZhbD1GQUxTRX0KY29ubmVjdGVkX2NvdW50IDwtIHN1cnZleV9kYXRhX3RpZHkgfD4KICBkaXN0aW5jdChJRCwgZmVlbF9xdWVzdGlvbiwgZmVlbF9yZXNwb25zZSkgfD4KICBmaWx0ZXIoZmVlbF9xdWVzdGlvbiA9PSAiZmVlbF9jb25uZWN0ZWQiKSB8PgogIGNvdW50KGZlZWxfcmVzcG9uc2UsIG5hbWUgPSAiZmVlbF9jb25uZWN0ZWQiKSB8PgogIGFycmFuZ2UoZGVzYyhmZWVsX2Nvbm5lY3RlZCkpCgpjb25uZWN0ZWRfY291bnQKYGBgCgoyKSBIb3cgbWFueSBzdHVkZW50cyBmZWVsICpkaXN0cmFjdGVkKiBieSB0aGVpciBzb2NpYWwgbWVkaWEgdXNlPwoKYGBge3IsIGNsYXNzLnNvdXJjZSA9ICdmb2xkLWhpZGUnLCBldmFsPUZBTFNFfQpkaXN0cmFjdGVkX2NvdW50IDwtIHN1cnZleV9kYXRhX3RpZHkgfD4KICBkaXN0aW5jdChJRCwgZmVlbF9xdWVzdGlvbiwgZmVlbF9yZXNwb25zZSkgfD4KICBmaWx0ZXIoZmVlbF9xdWVzdGlvbiA9PSAiZmVlbF9kaXN0cmFjdGVkIikgfD4KICBjb3VudChmZWVsX3Jlc3BvbnNlLCBuYW1lID0gImZlZWxfZGlzdHJhY3RlZCIpIHw+CiAgYXJyYW5nZShkZXNjKGZlZWxfZGlzdHJhY3RlZCkpCgpkaXN0cmFjdGVkX2NvdW50CmBgYAoKOjo6CgojIyBBc2tpbmcgYSBSZXNlYXJjaCBRdWVzdGlvbgoKU28gZmFyIHRoZSBxdWVzdGlvbnMgd2UgaGF2ZSBiZWVuIGFza2luZyBvZiBvdXIgZGF0YSBoYXZlIHJlbGF0ZWQgdG8gY291bnRzLCB3aGljaCBjYW4gYmUgdmVyeSBoZWxwZnVsLCBidXQgbWF5IG5vdCBnZXQgdG8gdGhlIGJpZ2dlciByZXNlYXJjaCBxdWVzdGlvbiB3ZSB3YW50IHRvIGFzay4gQXMgcGFydCBvZiB0aGUgYmlnZ2VyIHJlc2VhcmNoIHF1ZXN0aW9uLCB3ZSBtaWdodCB3YW50IHRvIGFzaywgKioiSG93IGRvZXMgaGVhdnkgdXNhZ2Ugb2Ygc29jaWFsIG1lZGlhIGFmZmVjdCBtZW50YWwgaGVhbHRoIGNvbXBhcmVkIHRvIGxpZ2h0IHVzYWdlPyoqCgpUaGVyZSB3aWxsIGJlIGEgZmV3IHN0ZXBzIHRvIGFuc3dlciB0aGlzIHF1ZXN0aW9uLCBidXQgbGV0J3MgYnJlYWsgdGhlbSBkb3duIGFuZCB0YWtlIHRoZW0gb25lIGF0IGEgdGltZToKCjEpIERlZmluZSBoZWF2eSB2cy4gbGlnaHQgdXNlcnMKMikgQmVjYXVzZSB0aGUgYGZlZWxfcmVzcG9uc2VgIHZhbHVlcyBhcmUgaW4gYSBMaWtlcnQgc2NhbGUsIHdlIG5lZWQgdG8gY29udmVydCB0aGVtIHRvIG51bWJlcnMgdG8gYWxsb3cgdXMgdG8gZ2V0IGF2ZXJhZ2VzLgozKSBHZXQgdGhlIGF2ZXJhZ2Ugc2NvcmUgZm9yIGVhY2ggYGZlZWxfcXVlc3Rpb25gIGZvciBib3RoIGxpZ2h0IGFuZCBoZWF2eSB1c2Vycy4KNCkgQ29tYmluZSB0aGUgZ3JvdXBzCjUpIFBsb3QgdGhlIGNvbXBhcmlzb24KCkxldCdzIGdldCBzdGFydGVkIQoKIyMjIERlZmluaW5nIGhlYXZ5IHZzLiBsaWdodCB1c2VycwoKSW4gdGhlIGBob3Vyc19wZXJfZGF5YCBxdWVzdGlvbiwgc3VydmV5IHJlc3BvbmRlbnRzIHdlcmUgZ2l2ZW4gZm91ciBwb3NzaWJsZSBhbnN3ZXJzOgoKKiBMZXNzIHRoYW4gMiBob3VycwoqIDItNCBob3VycwoqIDQtNiBob3VycwoqIE1vcmUgdGhhbiA2IGhvdXJzCgpGb3IgdGhlIHNha2Ugb2Ygb3VyIGFuYWx5c2lzLCBsZXQncyBzYXkgdGhhdCByZXNwb25kZW50cyB0aGF0IHNlbGVjdGVkICJMZXNzIHRoYW4gMiBob3VycyIgYW5kICIyLTQgaG91cnMiIGFyZSAqKmxpZ2h0IHVzZXJzKiosIGFuZCB0aG9zZSB0aGF0IHNlbGVjdGVkICI0LTYgaG91cnMiIGFuZCAiTW9yZSB0aGFuIDYgaG91cnMiIGFyZSAqKmhlYXZ5IHVzZXJzKiouIAoKOjo6d2Fsa3Rocm91Z2gKCkxldCdzIGNyZWF0ZSBhIG5ldyBjb2x1bW4gaW4gb3VyIGRhdGEgdG8gY2xhc3NpZnkgcmVzcG9uZGVudHMgYXMgImxpZ2h0IiBvciAiaGVhdnkiIHVzZXJzIGJhc2VkIG9uIHRoaXMgY3JpdGVyaWEuCgpgYGB7ciwgZXZhbD1UUlVFLCByZXN1bHRzPSJoaWRlIn0KCnN1cnZleV9kYXRhX3RpZHkgPC0gc3VydmV5X2RhdGFfdGlkeSB8PgogIG11dGF0ZSgKICAgIHVzYWdlX2dyb3VwID0gY2FzZV93aGVuKAogICAgICBob3Vyc19wZXJfZGF5ICVpbiUgYygiTGVzcyB0aGFuIDIgaG91cnMiLCAiMi00IGhvdXJzIikgfiAiTGlnaHQiLAogICAgICBob3Vyc19wZXJfZGF5ICVpbiUgYygiNC02IGhvdXJzIiwgIk1vcmUgdGhhbiA2IGhvdXJzIikgfiAiSGVhdnkiLAogICAgKQogICkKCmBgYAoKKipTdGVwLWJ5LXN0ZXAgZXhwbGFuYXRpb246KioKCiogYHN1cnZleV9kYXRhX3RpZHkgPC0gc3VydmV5X2RhdGFfdGlkeSB8PmAgaXMgdGVsbGluZyBSIHRoYXQgd2Ugd2FudCB0byBhZGQvZG8gc29tZXRoaW5nIG5ldyB0byBgc3VydmV5X2RhdGFfdGlkeWAgb2JqZWN0LCBhbmQgc2F2ZSBpdCBiYWNrIGludG8gdGhlIG9iamVjdC4KKiBUaGlzIGRhdGEgaXMgcGlwZWQgaW50byB0aGUgYG11dGF0ZSgpYCBmdW5jdGlvbiwgd2hpY2ggaXMgZ29pbmcgdG8gY3JlYXRlIGEgbmV3IGNvbHVtbiBpbiBvdXIgZGF0YSBjYWxsZWQgYHVzYWdlX2dyb3VwYC4KKiBFdmVyeXRoaW5nIGFmdGVyIHRoZSBgPWAgd2lsbCBiZSB3aGVyZSB3ZSBkZWZpbmUgd2hhdCBnb2VzIGludG8gdGhpcyBuZXcgY29sdW1uLgoqIFRoZSBgY2FzZV93aGVuKClgIGZ1bmN0aW9uIHdvcmtzIGluIHRoZSBmb2xsb3dpbmcgd2F5OiAiaWYgWCBjb25kaXRpb24gaXMgdHJ1ZSwgdGhlbiBkbyBZIi4gIAoqIGBob3Vyc19wZXJfZGF5ICVpbiVgIGNoZWNrcyBmb3IgdmFsdWVzIHRoYXQgYXJlIGluIHRoZSBgaG91cnNfcGVyX2RheWAgY29sdW1uLCB3aGljaCBhcmUgc3BlY2lmaWVkIGluIHRoZSBmb2xsb3dpbmcgYnJhY2tldHMuCiogVGhlIGBjYCBpbiBgYygiTGVzcyB0aGFuIDIgaG91cnMiLCAiMi00IGhvdXJzIilgIHRlbGxzIFIgd2UgYXJlIGxvb2tpbmcgZm9yIG11bHRpcGxlIHZhbHVlcyBpbiB0aGUgYGhvdXJzX3Blcl9kYXlgIGNvbHVtbiwgd2hpY2ggaW4gdGhpcyBjYXNlIGlzICJMZXNzIHRoYW4gMiBob3VycyIgYW5kICIyLTQgaG91cnMiLgoqIElmIGBob3Vyc19wZXJfZGF5YCBlcXVhbHMgZWl0aGVyIG9mIHRoZXNlIHZhbHVlcywgdGhleSB3aWxsIGJlIGdpdmUgdGhlIHZhbHVlICJMaWdodCIgaW4gdGhlIG5ldyBjb2x1bW4uCiogYGhvdXJzX3Blcl9kYXkgJWluJSBjKCI0LTYgaG91cnMiLCAiTW9yZSB0aGFuIDYgaG91cnMiKSB+ICJIZWF2eSJgIGRvZXMgdGhpcyBzYW1lIHByb2Nlc3MsIGJ1dCBpZiB0aGVzZSBuZXcgY29uZGl0aW9ucyBhcmUgbWV0LCB0aGV5IHdpbGwgYmUgZ2l2ZW4gdGhlIHZhbHVlICJIZWF2eSIgaW4gdGhlIG5ldyBjb2x1bW4uCgo6OjoKCkxldCdzIHRha2UgYSBsb29rIGF0IGBzdXJ2ZXlfZGF0YV90aWR5YCB0byBzZWUgdGhpcyBuZXcgY29sdW1uLgoKYGBge3IsIGV2YWw9RkFMU0V9ClZpZXcoc3VydmV5X2RhdGFfdGlkeSkKYGBgCgoKCiMjIyBDb252ZXJ0IEZlZWwgUmVzcG9uc2VzIHRvIE51bWJlcnMKClRoZSBmZWVsIHJlc3BvbnNlcyBhcmUgaW4gYSBMaWtlcnQvdGV4dHVhbCBmb3JtYXQsIGFuZCBjYWxjdWxhdGluZyB0aGUgYXZlcmFnZSBvZiB0ZXh0IGlzbid0IHNvbWV0aGluZyB0aGF0IGlzIHBvc3NpYmxlICh0aGUgdGV4dCBtdXN0IGJlIGNvdW50ZWQgb3IgcXVhbnRpZmllZCwgdGhlbiBjYWxjdWxhdGVkKS4gVG8gYWxsb3cgdXMgdG8gZmluZCB0aGUgYXZlcmFnZSwgd2UncmUgZ29pbmcgdG8gY3JlYXRlIGFub3RoZXIgY29sdW1uIHRoYXQgYXBwbGllcyBudW1lcmljYWwgdmFsdWVzIHRvIGVhY2ggTGlrZXJ0IHJlc3BvbnNlLgoKOjo6d2Fsa3Rocm91Z2gKCkNvbnZlcnRpbmcgTGlrZXJ0IHJlc3BvbnNlcyB0byBudW1iZXJzLgoKYGBge3IsIGV2YWw9VFJVRSwgcmVzdWx0cz0iaGlkZSJ9CnN1cnZleV9kYXRhX3RpZHkgPC0gc3VydmV5X2RhdGFfdGlkeSB8PgogIG11dGF0ZSgKICAgIGZlZWxfc2NvcmUgPSBjYXNlX3doZW4oCiAgICAgIGZlZWxfcmVzcG9uc2UgPT0gIlN0cm9uZ2x5IGRpc2FncmVlIiB+IDEsCiAgICAgIGZlZWxfcmVzcG9uc2UgPT0gIkRpc2FncmVlIiAgICAgICAgICB+IDIsCiAgICAgIGZlZWxfcmVzcG9uc2UgPT0gIk5ldXRyYWwiICAgICAgICAgICB+IDMsCiAgICAgIGZlZWxfcmVzcG9uc2UgPT0gIkFncmVlIiAgICAgICAgICAgICB+IDQsCiAgICAgIGZlZWxfcmVzcG9uc2UgPT0gIlN0cm9uZ2x5IGFncmVlIiAgICB+IDUsCiAgICApCiAgKQpgYGAKCioqU3RlcC1ieS1zdGVwIGV4cGxhbmF0aW9uOioqCgoqIGBzdXJ2ZXlfZGF0YV90aWR5IDwtIHN1cnZleV9kYXRhX3RpZHkgfD5gIGlzIHRlbGxpbmcgUiB0aGF0IHdlIHdhbnQgdG8gYWRkL2RvIHNvbWV0aGluZyBuZXcgdG8gYHN1cnZleV9kYXRhX3RpZHlgIG9iamVjdCwgYW5kIHNhdmUgaXQgYmFjayBpbnRvIHRoZSBvYmplY3QuCiogVGhlIGRhdGEgaXMgcGlwZWQgaW50byB0aGUgYG11dGF0ZSgpYCBmdW5jdGlvbiwgd2hpY2ggaXMgZ29pbmcgdG8gY3JlYXRlIGEgbmV3IGNvbHVtbi4KKiBUaGUgbmV3IGNvbHVtbiB3aWxsIGJlIGNhbGxlZCBgZmVlbF9zY29yZWAsIGFuZCB3aWxsIGNvbnRhaW4gYWxsIHRoZSBpbmZvcm1hdGlvbiBhZnRlciB0aGUgYD1gLgoqIE11Y2ggbGlrZSB0aGUgcHJldmlvdXMgZXhhbXBsZSwgd2UgYXJlIHVzaW5nIGBjYXNlX3doZW4oKWAgdG8gdGVsbCBSIHRoYXQgaWYgWCwgdGhlbiBZLgoqIEluIHRoaXMgY2FzZSBgZmVlbF9yZXNwb25zZSA9PSAiU3Ryb25nbHkgZGlzYWdyZWUiIH4gMWAsIHdlIGFyZSBzYXlpbmcsICJpZiB0aGUgYGZlZWxfcmVzcG9uc2VgIGNvbHVtbiBoYXMgYSB2YWx1ZSB0aGF0IGlzICJTdHJvbmdseSBkaXNhZ3JlZSIsIHdlIHdhbnQgdG8gYWRkIHRoZSB2YWx1ZSAiMSIgaW4gdGhlIG5ldyBjb2x1bW4uCiogUmVwZWF0IGZvciBlYWNoIHZhbHVlIGluIHRoZSBMaWtlcnQgc2NhbGUsIGFuZCB3ZSdyZSByZWFkeSB0byBjYWxjdWxhdGUhCgo6OjoKCkxldCdzIHRha2UgYW5vdGhlciBsb29rIGF0IGBzdXJ2ZXlfZGF0YV90aWR5YC4KCmBgYHtyLCBldmFsPUZBTFNFfQoKVmlldyhzdXJ2ZXlfZGF0YV90aWR5KQoKYGBgCgpXZSdyZSBnZXR0aW5nIGNsb3NlISBMZXQncyBub3cgY2FsY3VsYXRlIHRoZSBhdmVyYWdlIGZvciBsaWdodCB1c2Vycy4gV2UnbGwgZG8gdGhpcyBpbiB0d28gZGlzdGluY3Qgc3RlcHMuCgo6Ojp3YWxrdGhyb3VnaAoKU3RlcCAxOiBJc29sYXRlIGxpZ2h0IHVzZXJzIGludG8gdGhlaXIgb3duIGdyb3VwaW5nCgpgYGB7ciwgZXZhbD1UUlVFLCByZXN1bHRzPSJoaWRlIn0KbGlnaHRfdXNlcnMgPC0gc3VydmV5X2RhdGFfdGlkeSB8PgogIGZpbHRlcih1c2FnZV9ncm91cCA9PSAiTGlnaHQiKQpgYGAKCioqU3RlcC1ieS1zdGVwIGV4cGxhbmF0aW9uOioqCgoqIFdlJ3JlIGNyZWF0aW5nIGEgbmV3IFIgb2JqZWN0IGNhbGxlZCBgbGlnaHRfdXNlcnNgLCB0aGF0IHdpbGwgaGVscCB1cyBlYXNpbHkgY2FsY3VsYXRlIHZhbHVlcyBpbiB0aGlzIGdyb3VwLgoqIEFmdGVyIHBpcGluZyAgYHN1cnZleV9kYXRhX3RpZHlgLCB3ZSB1c2UgdGhlIGBmaWx0ZXIodXNlcl9ncm91cCA9PSAiTGlnaHQiKWAgdG8gc2VsZWN0IG9ubHkgdGhvc2Ugcm93cyBpbiB0aGUgYHVzZXJfZ3JvdXBgIGNvbHVtbiB0aGF0IGhhdmUgdGhlIHZhbHVlICJMaWdodCIuCgo6OjoKCldlIGNhbiB2aWV3IHRoaXMgbmV3IG9iamVjdDoKYGBge3IsIGV2YWw9RkFMU0V9ClZpZXcobGlnaHRfdXNlcnMpCmBgYAoKOjo6d2Fsa3Rocm91Z2gKClN0ZXAgMjogQ2FsY3VsYXRlIHRoZSBhdmVyYWdlIG9mIGxpZ2h0IHVzZXJzJyBmZWVsIHJlc3BvbnNlcwoKYGBge3IsIGV2YWw9VFJVRSwgcmVzdWx0cz0iaGlkZSJ9CmxpZ2h0X2F2Z3MgPC0gbGlnaHRfdXNlcnMgfD4KICBncm91cF9ieShmZWVsX3F1ZXN0aW9uKSB8PgogIHN1bW1hcmlzZSgKICAgIGF2Z19mZWVsID0gbWVhbihmZWVsX3Njb3JlKSwKICAgIC5ncm91cHMgPSAiZHJvcCIpIHw+CiAgbXV0YXRlKHVzYWdlX2dyb3VwID0gIkxpZ2h0IikKYGBgCgoqKlN0ZXAtYnktc3RlcCBleHBsYW5hdGlvbjoqKgoKKiBXZSBhcmUgY3JlYXRpbmcgYSBuZXcgb2JqZWN0IGNhbGxlZCBgbGlnaHRfYXZnc2AgdGhhdCB3aWxsIGhvbGQgdGhlIGF2ZXJhZ2VzIGZvciBhbGwgZmVlbCByZXNwb25zZXMgZm9yIGxpZ2h0IHVzZXJzLgoqIFBpcGUgdGhlIGRhdGEgZnJvbSB0aGUgYGxpZ2h0X3VzZXJzYCBvYmplY3QgaW50byBgZ3JvdXBfYnkoZmVlbF9xdWVzdGlvbilgLCB3aGljaCB0ZWxscyBSIHRvIGNhbGN1bGF0ZSByZXN1bHRzIHNlcGFyYXRlbHkgZm9yIGVhY2ggZmVlbCBxdWVzdGlvbi4KKiBUaGlzIGlzIHBpcGVkIGludG8gdGhlIGBzdW1tYXJpc2UoKWAgZnVuY3Rpb24sIHdoaWNoIHNldHMgdXAgZm9yIHRoZSBhY3R1YWwgY2FsY3VsYXRpb24gd2Ugd2FudCB0byBjb25kdWN0LgoqIGBhdmdfZmVlbGAgd2lsbCBjcmVhdGUgYSBuZXcgY29sdW1uIHdpdGggdGhlIHZhbHVlcyBvbiB0aGUgcmlnaHQgc2lkZSBvZiB0aGUgYD1gLgoqIGBtZWFuKGZlZWxfc2NvcmUpYCBjYWxjdWxhdGVzIHRoZSBhdmVyYWdlIHNjb3JlIGZvciBlYWNoIGZlZWwgcmVzcG9uc2UuIFRoaXMgd2lsbCBnaXZlIGEgdGFibGUgd2l0aCBvbmUgcm93IHBlciBgZmVlbF9xdWVzdGlvbmAsIGFuZCBvbmUgY29sdW1uIGNvbnRhaW5pbmcgdGhlIGF2ZXJhZ2UgZm9yIGVhY2ggKGBhdmdfZmVlbGApLgoqIGAuZ3JvdXBzID0gImRyb3AiYCB0ZWxscyBSIHRvIGdldCByaWQgb2YgdGhlIGdyb3VwaW5nIHN0cnVjdHVyZSBpdCB1c2VzIHRvIGNhbGN1bGF0ZSB0aGUgdmFsdWVzLiBJbiBwbGFpbiBsYW5ndWFnZSwgIndlJ3JlIGRvbmUgZ3JvdXBpbmcsIGdpdmUgbWUgYSByZWd1bGFyIHRhYmxlIG5vdyIuCiogVGhpcyBpcyBwaXBlZCBpbnRvIGBtdXRhdGUodXNlcl9ncm91cCA9ICJMaWdodCIpYCB0byBhZGQgYSBuZXcgY29sdW1uIHRvIHRoZSB0YWJsZSBjYWxsZWQgYHVzZXJfZ3JvdXBgLCBhbmQgZXZlcnkgcm93IHdpbGwgZ2V0IHRoZSB2YWx1ZSAiTGlnaHQiLiBUaGlzIHN0ZXAgaXMgaGVscGZ1bCBiZWNhdXNlIHdoZW4gd2UgY29tYmluZSB0aGUgbGlnaHQgYW5kIGhlYXZ5IHVzZXJzIGludG8gb25lIGRhdGFzZXQgZm9yIHBsb3R0aW5nLCB0aGlzIHdpbGwga2VlcCBhbGwgdGhlIHZhbHVlcyBzb3J0ZWQgYnkgdGhlaXIgdXNhZ2UgZ3JvdXBzLgoKOjo6CgpMZXQncyB0YWtlIGEgbG9vayBhdCB0aGUgbmV3IHRhYmxlIHdlIGNyZWF0ZWQhCgpgYGB7ciwgZXZhbD1GQUxTRX0KbGlnaHRfYXZncwpgYGAKCiMjIFlvdXIgVHVybiEKCjo6OnF1ZXN0aW9uCgpOb3cgdGhhdCB5b3UndmUgc2VlbiBob3cgdGhpcyB3b3JrcyBmb3IgdGhlIGxpZ2h0IHVzZXJzLCBzZWUgaWYgeW91IGNhbiBkbyB0aGlzIGZvciBoZWF2eSB1c2Vycy4gVGhpcyBpcyBhIHNlY3JldCBvZiB1c2luZyBhIGNvZGluZyBsYW5ndWFnZSwgd2hlcmUgeW91IGRvbid0IG5lZWQgdG8gd3JpdGUgZXZlcnkgbGluZSBmcm9tIHNjcmF0Y2gsIGFuZCB5b3UganVzdCBuZWVkIHRvIHVuZGVyc3RhbmQgY29kZSBjaHVua3MgZW5vdWdoIHRvIGtub3cgd2hhdCBwYXJ0cyB0byBjaGFuZ2UuCgoxKSBJc29sYXRlIGhlYXZ5IHVzZXJzIGludG8gdGhlaXIgb3duIGdyb3VwLCBhbmQgc2F2ZSBpdCBhcyBhbiBvYmplY3QgY2FsbGVkIGBoZWF2eV91c2Vyc2AKYGBge3IsIGNsYXNzLnNvdXJjZSA9ICdmb2xkLWhpZGUnLCBldmFsPVRSVUUsIHJlc3VsdHM9ImhpZGUifQpoZWF2eV91c2VycyA8LSBzdXJ2ZXlfZGF0YV90aWR5IHw+CiAgZmlsdGVyKHVzYWdlX2dyb3VwID09ICJIZWF2eSIpCmBgYAoKMikgQ2FsY3VsYXRlIHRoZSBhdmVyYWdlIG9mIGhlYXZ5IHVzZXJzJyBmZWVsIHJlc3BvbnNlcywgYW5kIHNhdmUgaXQgYXMgYW4gb2JqZWN0IGNhbGxlZCBgaGVhdnlfYXZnc2AuCmBgYHtyLCBjbGFzcy5zb3VyY2UgPSAnZm9sZC1oaWRlJywgZXZhbD1UUlVFLCByZXN1bHRzPSJoaWRlIn0KaGVhdnlfYXZncyA8LSBoZWF2eV91c2VycyB8PgogIGdyb3VwX2J5KGZlZWxfcXVlc3Rpb24pIHw+CiAgc3VtbWFyaXNlKAogICAgYXZnX2ZlZWwgPSBtZWFuKGZlZWxfc2NvcmUpLAogICAgLmdyb3VwcyA9ICJkcm9wIikgfD4KICBtdXRhdGUodXNhZ2VfZ3JvdXAgPSAiSGVhdnkiKQpgYGAKCjo6OgoKCiMjIyBDb21iaW5lIHRoZSBHcm91cHMKCldlJ3JlIGdldHRpbmcgY2xvc2UhIFRoZSBuZXh0IHN0ZXAgd2UgbmVlZCB0byBkbyBpcyBjb21iaW5lIHRoZSBsaWdodCBhbmQgaGVhdnkgdXNhZ2UgZ3JvdXBzIHRvIG1ha2UgaXQgdmVyeSBlYXN5IHRvIHBsb3QuIFRoaXMgY2FuIGJlIGRvbmUgd2l0aCBhIHNpbmdsZSBsaW5lIG9mIGNvZGUuCgo6Ojp3YWxrdGhyb3VnaAoKQ29tYmluaW5nIHRoZSBsaWdodCBhbmQgaGVhdnkgdXNlciBhdmVyYWdlcy4KCmBgYHtyLCBldmFsPVRSVUUsIHJlc3VsdHM9ImhpZGUifQpmZWVsX2F2Z3MgPC0gYmluZF9yb3dzKGxpZ2h0X2F2Z3MsIGhlYXZ5X2F2Z3MpCmBgYAoKKipTdGVwLWJ5LXN0ZXAgZXhwbGFuYXRpb246KioKCiogV2UncmUgY3JlYXRpbmcgYSBuZXcgUiBvYmplY3QgY2FsbGVkIGBmZWVsX2F2Z3NgIHRoYXQgd2lsbCBoYXZlIHRoZSBhdmVyYWdlcyBmb3IgYWxsIGZlZWwgcmVzcG9uc2VzIGZvciBib3RoIGxpZ2h0IGFuZCBoZWF2eSB1c2Vycy4KKiB0aGUgYGJpbmRfcm93cygpYCBmdW5jdGlvbiB0YWtlcyB0d28gb3IgbW9yZSBkYXRhIGZyYW1lcyB0aGF0IGhhdmUgdGhlICoqc2FtZSBjb2x1bW5zKiwgYW5kIGNvbWJpbmVzIHRoZW0gYnkgc3RhY2tpbmcgdGhlbSB2ZXJ0aWNhbGx5LgoqIGBsaWdodHNfYXZnc2AgYW5kIGBoZWF2eV9hdmdzYCBhcmUgdGhlIHR3byBvYmplY3RzIHRoYXQgaGF2ZSB0aGUgYXZlcmFnZXMsIGFuZCB0aGlzIHdpbGwgZWZmZWN0aXZlbHkgY29tYmluZSB0aGVtLgoKOjo6CgoKTGV0J3MgdGFrZSBhIGxvb2suCgpgYGB7ciwgZXZhbD1GQUxTRX0KZmVlbF9hdmdzCmBgYAoKIyMjIFBsb3R0aW5nIHRoZSBSZXN1bHRzCgpOb3cgdGhhdCB3ZSBoYXZlIG91ciBkYXRhIHJlYWR5IHRvIGdvLCB3ZSBjYW4gbm93IHB1dCBpdCBpbnRvIGEgcGxvdC4gRWFybGllciBpbiB0aGlzIHNlc3Npb24gd2UgdXNlZCB0aGUgYGJhcnBsb3QoKWAgZnVuY3Rpb24sIHdoaWNoIGlzIGdyZWF0IGFzIGEgcXVpY2sgd2F5IHRvIHZpc3VhbGl6ZSBkYXRhIGluIGJhciBjaGFydHMuIEhvd2V2ZXIsIGEgbXVjaCBtb3JlIHBvd2VyZnVsIHZpc3VhbGl6YXRpb24gcGFja2FnZSwgdGhhdCBpcyBwYXJ0IG9mIHRoZSBUaWR5dmVyc2UsIGlzIGdncGxvdDIuIAoKVGhpcyB3aWxsIG5vdCBiZSBhIGRlZXAgZGl2ZSBpbnRvIGdncGxvdDIsIGJ1dCBpZiB5b3UgYXJlIGludGVyZXN0ZWQgaW4gbGVhcm5pbmcgbW9yZSwgY2hlY2sgb3V0IHRoZSBzZXNzaW9uIG9uIDxhIGhyZWY9IkJsb2NrNS0yX2dncGxvdC5odG1sIj5WaXN1YWxpemF0aW9uIHdpdGggZ2dwbG90PC9hPi4KCmdncGxvdCBvZmZlcnMgYSBudW1iZXIgb2YgZGlmZmVyZW50IHZpc3VhbGl6YXRpb24gdHlwZXMsIGFuZCBhcyBpdCB3b3JrcyBpbiAibGF5ZXJzIiwgeW91IGNhbiBrZWVwIGFkZGluZyBtb3JlIGVsZW1lbnRzIHRvIHlvdXIgdmlzdWFsaXphdGlvbnMsIG1ha2luZyBpdCBhbiBleHRyZW1lbHkgcG93ZXJmdWwgYW5kIHByZWNpc2UgdG9vbC4gQXMgbWVudGlvbmVkLCB0aGlzIHdvbid0IGJlIGEgZGVlcCBkaXZlLCBidXQgd2UnbGwgdGFrZSBhIGxvb2sgYXQgd2hhdCB2aXN1YWxpemluZyBvdXIgZGF0YSBpbiBnZ3Bsb3QgY2FuIGRvIGZvciBvdXIgZGF0YS4KCjo6OndhbGt0aHJvdWdoCgpWaXN1YWxpemluZyB0aGUgcXVlc3Rpb25zICJIb3cgZG9lcyBoZWF2eSB1c2FnZSBvZiBzb2NpYWwgbWVkaWEgYWZmZWN0IG1lbnRhbCBoZWFsdGggY29tcGFyZWQgdG8gbGlnaHQgdXNhZ2U/IgoKYGBge3IsIGV2YWw9RkFMU0V9CmdncGxvdChmZWVsX2F2Z3MsIGFlcyh4ID0gZmVlbF9xdWVzdGlvbiwgeSA9IGF2Z19mZWVsLCBmaWxsID0gdXNhZ2VfZ3JvdXApKSArCiAgZ2VvbV9jb2wocG9zaXRpb24gPSAiZG9kZ2UiKQpgYGAKCioqU3RlcC1ieS1zdGVwIGV4cGxhbmF0aW9uOioqCgoqIEV2ZXJ5IHZpc3VhbGl6YXRpb24gd2l0aCBnZ3Bsb3Qgc3RhcnRzIHdpdGggdGhlIGBnZ3Bsb3QoKWAgY29tbWFuZCB0byB0ZWxsIFIgdGhhdCB3ZSdyZSBnb2luZyB0byBtYWtlIGEgcGxvdC4KKiBUaGUgZmlyc3QgZWxlbWVudCB0byBnbyBpbiB0aGUgYnJhY2tldHMgaXMgdGhlIGRhdGEgd2Ugd2FudCB0byBwbG90LCB3aGljaCBpbiB0aGlzIGNhc2UgaXMgYGZlZWxfYXZnc2AuCiogQWZ0ZXIgYSBjb21tYSBgLGAgd2UgdXNlIHRoZSBgYWVzKClgIGZ1bmN0aW9uIHRvIGRlc2NyaWJlIHRoZSBhZXN0aGV0aWNzIG9mIHdoYXQgdGhlIHBsb3Qgd2lsbCBsb29rIGxpa2UuIEluIHRoaXMgY2FzZSwgd2Ugc3BlY2lmeSB0aGF0IHRoZSB4LWF4aXMgd2lsbCBjb250YWluIGBmZWVsX3F1ZXN0aW9uYCBjb2x1bW4gaW4gdGhlIHRhYmxlLCBhbmQgdGhlIHktYXhpcyB3aWxsIGNvbnRhaW4gYGF2Z19mZWVsYCwgd2hpY2ggaXMgdGhlIGF2ZXJhZ2VzLiBgZmlsbCA9IHVzYWdlX2dyb3VwYCBpbmRpY2F0ZXMgdGhhdCB3ZSB3YW50IGVhY2ggdXNhZ2UgZ3JvdXAgdG8gYmUgZmlsbGVkIHdpdGggYSBkaWZmZXJlbnQgY29sb3VyLgoqIE5vdyB0aGF0IHdlJ3ZlIHNwZWNpZmllZCB0aGUgYWVzdGhldGljcywgd2UgaW5zZXJ0IGEgcGx1cyBzaWduIGArYCB0byBhZGQgYWRkaXRpb25hbCBlbGVtZW50cy4KKiBgZ2VvbV9jb2woKWAgc3BlY2lmaWVzIHRoZSBnZW9tLCBvciB0eXBlIG9mIGNoYXJ0IHdlIHdhbnQgdG8gbWFrZSAodGhlcmUgYXJlIG1hbnkhKSwgYW5kIGBwb3NpdGlvbiA9ICJkb2RnZSJgIHRlbGxzIFIgdGhhdCB3ZSB3YW50IGEgc2VwYXJhdGUgY29sdW1uIGZvciBsaWdodCBhbmQgaGVhdnkgdXNlcnMsIGluc3RlYWQgb2YgYSBzdGFja2VkIGNvbHVtbi4KCjo6OgoKWW91J2xsIHNlZSB0aGF0IHRoZXJlJ3Mgbm93IGEgcGxvdCBpbiB0aGUgdmlld2VyIHBhbmUgaW4gUlN0dWRpbyAoYm90dG9tIHJpZ2h0KS4gQmVmb3JlIHdlIGdldCB0byB0aGUgZGF0YSBpbnNpZ2h0cyBmcm9tIHRoZSBwbG90LCBpdCdzIHdvcnRoIG5vdGluZyB0aGF0IHRoZXJlJ3MgYW4gaXNzdWUgd2l0aCB0aGUgY2hhcnQuIE9uIHRoZSB5LWF4aXMsIHdlIHNlZSBudW1iZXJzLCB3aGljaCBhcmVuJ3QgdmVyeSBnb29kIGF0IGNvbW11bmljYXRpbmcgdGhlIExpa2VydCBzY2FsZSB3ZSdyZSB2aXN1YWxpemluZywgYW5kIHRoZSBudW1iZXJzIGVuZCBhdCAzICh0aGV5IGFyZSBjaG9wcGVkIG9mZiBiZWNhdXNlIHRoYXQncyB3aGVyZSB0aGUgYXZlcmFnZXMgc3RvcCkuIFRvIGNvbW11bmljYXRlIHRoZXNlIGZpbmRpbmdzIG1vcmUgZWZmZWN0aXZlbHksIGxldCdzIHVwZGF0ZSB0aGUgeS1heGlzIHRvIGJlIG1vcmUgZGVzY3JpcHRpdmUgYW5kIGVhc2llciB0byBpbnRlcnByZXQuCgo6Ojp3YWxrdGhyb3VnaAoKQ2xlYW5pbmcgdXAgdGhlIHktYXhpcyBvZiBvdXIgcGxvdAoKYGBge3IsIGV2YWw9RkFMU0V9CmdncGxvdChmZWVsX2F2Z3MsIGFlcyh4ID0gZmVlbF9xdWVzdGlvbiwgeSA9IGF2Z19mZWVsLCBmaWxsID0gdXNhZ2VfZ3JvdXApKSArCiAgZ2VvbV9jb2wocG9zaXRpb24gPSAiZG9kZ2UiKSArCiAgc2NhbGVfeV9jb250aW51b3VzKAogICAgYnJlYWtzID0gMTo1LAogICAgbGFiZWxzID0gYygKICAgICAgIjEgPSBTdHJvbmdseSBkaXNhZ3JlZSIsCiAgICAgICIyID0gRGlzYWdyZWUiLAogICAgICAiMyA9IE5ldXRyYWwiLAogICAgICAiNCA9IEFncmVlIiwKICAgICAgIjUgPSBTdHJvbmdseSBhZ3JlZSIKICAgICkKICApICsKICBleHBhbmRfbGltaXRzKHkgPSA1KQpgYGAKCioqU3RlcC1ieS1zdGVwIGV4cGxhbmF0aW9uOioqCgpCZWdpbm5pbmcgd2hlcmUgd2UgbGVmdCBvZmYgYXQgdGhlIGZpcnN0IHBsb3QsCgoqIFdlIGFuZCBhIHBsdXMgc2lnbiBgK2AgdG8gdGVsbCBSIHdlIHdhbnQgdG8gYWRkIGFub3RoZXIgbGF5ZXIuCiogYHNjYWxlX3lfY29udGludW91cyhgIHRlbGxzIFIgd2Ugd2FudCB0byBjaGFuZ2UgaG93IHRoZSB5LWF4aXMgbG9va3MsIGFuZCBldmVyeXRoaW5nIGluIHRoZXNlIGJyYWNrZXRzIHdpbGwgYWRkIHRvIHRoaXMuCiogYGJyZWFrcyA9IDE6NWAgdGVsbHMgUiB0aGF0IHdlIHdhbnQgdG8gcHV0IHRpY2sgbWFya3MgYXQgdGhlIHZhbHVlcyAxLCAyLCAzLCA0LCA1LiBgMTo1YCBpcyBzaG9ydGhhbmQgZm9yIGFsbCB0aGUgbnVtYmVycyBiZXR3ZWVuIDEtNS4gCiogRG9uJ3QgZm9yZ2V0IHRvIHNlcGFyYXRlIHRoZSBsaW5lIHdpdGggdGhlIGNvbW1hIGAsYCEKKiBgbGFiZWxzID0gYygiMSA9IFN0cm9uZ2x5IGRpc2FncmVlLCAuLi4pYCByZXBsYWNlcyB0aGUgbnVtYmVycyBvbiB0aGUgeS1heGlzIHdpdGggd2hhdGV2ZXIgdGV4dHVhbCBsYWJlbHMgYXJlIHB1dCBpbiB0aGUgcXVvdGF0aW9uIG1hcmtzLiBCZWNhdXNlIHdlIHNwZWNpZmllZCBgYnJlYWtzID0gMTo1YCwgaXQgd2lsbCBhY2NlcHQgZml2ZSB2YWx1ZXMuIFRoaXMgY2FuIGJlIGV4cGFuZGVkIG9yIHJlZHVjZWQgYWNjb3JkaW5nIHRvIHlvdXIgcGxvdC4KKiBUaGVyZSBhcmUgdHdvIGVuZCBicmFja2V0cyBgKSlgIHRoYXQgbmVlZCB0byBiZSBjbG9zZWQuIE9uZSBmb3IgYHNjYWxlX3lfY29udGludW91cyhgIGNvbW1hbmQsIGFuZCBvbmUgZm9yIGBsYWJlbHMgPSBjKGAuCiogQWRkIGFub3RoZXIgcGx1cyBzaWduIGArYCB0byBhZGQgYW5vdGhlciBsYXllci4KKiBgZXhwYW5kX2xpbWl0cyh5ID0gNSlgIGV4cGFuZHMgdGhlIGNoYXJ0IHRvIGZpdmUgdGljayBtYXJrcywgdG8gbWFrZSBzdXJlIHdlIGNhbiBzaG93IHRoZSBmdWxsIHNjYWxlLgoKOjo6CgojIyBZb3VyIFR1cm4KCjo6OnF1ZXN0aW9uCgpUaGUgZ29hbCBvZiB0aGlzIHF1ZXN0aW9uIGlzIHRvIHRlc3QgeW91cnNlbGYgaW4gZGVjaXBoZXJpbmcgY29kZSB0aGF0IHlvdSBtaWdodCBub3QgYmUgZmFtaWxpYXIgd2l0aC4gCgoxKSBDb3B5ICsgcGFzdGUgdGhpcyBjb2RlIGluIHlvdXIgUlN0dWRpbyBlZGl0b3IgYW5kIHJ1biBpdCB0byBzZWUgdGhlIHJlc3VsdHMuCjIpIFRyeSB0byBkZXRlcm1pbmUgd2hhdCBlYWNoIHBhcnQgb2YgdGhlIGNvZGUgaXMgZG9pbmcuCgpIaW50OiBFYWNoIGZ1bmN0aW9uYWwgZWxlbWVudCwgb3IgbGF5ZXIsIG9mIGdncGxvdCBpcyBzZXBhcmF0ZWQgYnkgdGhlIHBsdXMgc2lnbiBgK2AsIGFuZCBzZWFyY2ggZW5naW5lcyBhcmUgeW91ciBmcmllbmQhCgpgYGB7ciwgZXZhbD1GQUxTRX0KYXZnX2ZlZWxfcGxvdCA8LSBnZ3Bsb3QoZmVlbF9hdmdzLCBhZXMoeCA9IGZlZWxfcXVlc3Rpb24sIHkgPSBhdmdfZmVlbCwgZmlsbCA9IHVzYWdlX2dyb3VwKSkgKwogIGdlb21fY29sKHBvc2l0aW9uID0gImRvZGdlIikgKwogIHNjYWxlX3lfY29udGludW91cygKICAgIGJyZWFrcyA9IDE6NSwKICAgIGxhYmVscyA9IGMoCiAgICAgICIxID0gU3Ryb25nbHkgZGlzYWdyZWUiLAogICAgICAiMiA9IERpc2FncmVlIiwKICAgICAgIjMgPSBOZXV0cmFsIiwKICAgICAgIjQgPSBBZ3JlZSIsCiAgICAgICI1ID0gU3Ryb25nbHkgYWdyZWUiCiAgICApCiAgKSArCiAgZXhwYW5kX2xpbWl0cyh5ID0gNSkgKwogIHNjYWxlX3hfZGlzY3JldGUoCiAgICBsYWJlbHMgPSBjKAogICAgICBmZWVsX2Nvbm5lY3RlZCAgICAgID0gIk1ha2VzIG1lIGZlZWwgY29ubmVjdGVkIiwKICAgICAgZmVlbF9kaXN0cmFjdGVkICAgICA9ICJNYWtlcyBtZSBmZWVsIGRpc3RyYWN0ZWQiLAogICAgICBmZWVsX2ltcHJvdmVkX21vb2QgID0gIkltcHJvdmVzIG15IG1vb2QiLAogICAgICBmZWVsX2luY3JlYXNlX3N0cmVzcyA9ICJJbmNyZWFzZXMgbXkgc3RyZXNzIikpICsKICBzY2FsZV9maWxsX2Rpc2NyZXRlKAogICAgbmFtZSAgID0gTlVMTCwKICAgIGxhYmVscyA9IGMoIkhlYXZ5IHVzZXJzIiwgIkxpZ2h0IHVzZXJzIikpICsKICBsYWJzKAogICAgeCAgICAgPSAiQWdyZWUgb3IgZGlzYWdyZWUgYWJvdXQgc29jaWFsIG1lZGlhIGFuZCB5b3VyIG1lbnRhbCBoZWFsdGg6IiwKICAgIHkgICAgID0gIkF2ZXJhZ2UgZmVlbGluZyByZXNwb25zZSIsCiAgICB0aXRsZSA9ICJTb2NpYWwgbWVkaWEgYW5kIG1lbnRhbCBoZWFsdGggYWNyb3NzIGxpZ2h0IGFuZCBoZWF2eSB1c2VycyIpICsKICB0aGVtZSggYXhpcy50aXRsZS55ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gMCwgdmp1c3QgPSAwLjUpKQoKYXZnX2ZlZWxfcGxvdApgYGAKCgo6OjoKCiMjIFdyYXBwaW5nIFVwCgojIyMgU2F2aW5nIGZpbGVzCgpXZSBoYXZlbid0IGdlbmVyYXRlZCBhcyBtYW55IGZpbGVzIGFzIHdlIGRpZCBpbiB0aGUgcHJldmlvdXMgc2Vzc2lvbiwgYnV0IHlvdSdsbCB3YW50IHRvIHNhdmUgdHdvIHRoaW5nczoKCjEpIFRoZSBSIHNjcmlwdCBmb3IgdGhpcyBzZXNzaW9uOiBgZmlsZSA+IHNhdmVgCjIpIFRoZSBgc3VydmV5X2RhdGFfdGlkeS5jc3ZgIGZpbGUgdGhhdCBoYXMgbmV3IGNvbHVtbnM6IApgYGB7ciwgZXZhbD1GQUxTRX0Kd3JpdGVfY3N2KHN1cnZleV9kYXRhX3RpZHksICJzdXJ2ZXlfZGF0YV9hbmFseXplZC5jc3YiKQpgYGAKMykgVGhlIGZpbmFsIHBsb3Qgd2UgY3JlYXRlZDoKYGBge3IsIGV2YWw9RkFMU0V9Cmdnc2F2ZShmaWxlbmFtZSA9ICJhdmdfZmVlbF9wbG90LnBkZiIsIAogIHBsb3QgPSBhdmdfZmVlbF9wbG90LCAKICB3aWR0aCA9IDgsIAogIGhlaWdodCA9IDYpCmBgYAoKSW4gYWRkaXRpb24gdG8gdGhlc2UgZmlsZXMsIHlvdSBjYW4gaW1hZ2luZSB0aGF0IHlvdSBtaWdodCB3YW50IHRvIHNhdmUgb3RoZXIgc25hcHNob3RzIG9mIHRoZSBkYXRhIChpZS4gYGxpZ2h0X3VzZXJzYCwgYGhlYXZ5X3VzZXJzYCwgZXRjLikuCgojIyMgUmV2aXNpdGluZyBvdXIgZmlsZSBsaXN0OiAKCi0gYGF2Z19mZWVsX3Bsb3QucGRmYAotIGBzb2NpYWwtbWVkaWEtc3VydmV5X09SSUdJTkFMLmNzdmAKLSBgc29jaWFsLW1lZGlhLXN1cnZleS5jc3ZgCi0gYHN1cnZleV9hbmFseXNpc19zY3JpcHQuUmAKLSBgc3VydmV5X2RhdGFfYW5hbHl6ZWQuY3N2YAotIGBzdXJ2ZXlfZGF0YV90aWR5LmNzdmAKLSBgc3VydmV5X2RhdGFfd2lkZS5jc3ZgCi0gYHN1cnZleV9kYXRhLlJwcm9qYAotIGBzdXJ2ZXlfY2xlYW5pbmdfc2NyaXB0LlJgCi0gYHN1cnZleS1kYXRhX2NsZWFuLWNvbHNfSURzLmNzdmAKLSBgc3VydmV5LWRhdGFfY2xlYW4tY29sc19uby1JRC5jc3ZgCgpZb3UgY2FuIHNlZSB0aGF0IHRoZXJlJ3Mgc29tZSBpbmNvbnNpc3RlbmN5IHdpdGggaG93IGZpbGVzIGFyZSBuYW1lZCAoaHlwaGVucyBgLWAgdnMgdW5kZXJzY29yZXMgYF9gKSwgYW5kIHlvdSBtaWdodCB3YW50IHRvIHRoaW5rIGFib3V0IGNyZWF0aW5nIGZvbGRlcnMgdG8gc3RhcnQgb3JnYW5pemluZyB0aGluZ3MuIFdoaWxlIHRoaXMgaXMgdGhlIGVuZCBvZiB0aGUgc2Vzc2lvbiwgZmVlbCBmcmVlIHRvIGdpdmUgdGhpcyBzb21lIHRob3VnaHQgYW5kIG1hcCBvdXQgaG93IHlvdSBtaWdodCB3YW50IHRvIHN0cnVjdHVyZSB0aGVzZSBmaWxlcyBpbiBmb2xkZXJzLCBhbmQgcG90ZW50aWFsbHkgcmVuYW1lIHRoZW4gdG8gYmV0dGVyIHN1aXQgeW91ciB1c2UuCgojIyBGaW5pc2gKCkFuZCB0aGF0J3MgaXQgZm9yIHRoZSB3b3Jrc2hvcCEgV2UgY292ZXJlZCBhIGxvdCBvZiBncm91bmQsIGJ1dCB0aGVyZSBpcyBhbHNvIGEgbG90IHRoYXQgd2Ugd2VyZW4ndCBhYmxlIHRvIGNvdmVyLiBUaGUgaG9wZSBpcyB0aGF0IHRoZXNlIHNlc3Npb25zIGhlbHBlZCBidWlsZCBzb21lIGNvbmZpZGVuY2UgaW4geW91ciBhYmlsaXR5IHRvIHdvcmsgd2l0aCBSLCBhbmQgdGhhdCB5b3UgdXNlIHRoaXMgYXMgYSBqdW1waW5nIG9mZiBwb2ludCBmb3IgeW91ciBsZWFybmluZyBqb3VybmV5LgoKCgoK