Solutions to this workshop can be found here

The techniques we’ve learned in the last few workshops are geared towards helping you understand the basics of how to analyze data in R. However, R is a full-fledged programming language, and you can use it to do a lot more than just plot data and run stats.

One technique that can be incredibly helpful in planning experiments, understanding data, and trying out statistical approaches is simulation. In many cases, by simulating what we expect our data to look like (ideally before even setting up any experiments), we can quickly figure out confounding factors in experimental design we had not yet thought about; estimate the sample sizes we would need to detect effects that interest us, if those effects indeed exist; and start to think about and program the statistical analyses we’d have to perform on our real data once we have it.

These final workshops cover some ideas that are fundamental to programming in any language through the lens of simulating the expected results of an experiment. We’ll also try to come back to some of the concepts we’ve already covered, but a key point is that while most of the material in workshops 5-7 is quite specific to R, the ideas we’ll cover in this workshop will carry over (with some syntax differences) to any other computer language you learn.

Conditional (If/Else) Statements

One very useful construct in programming is a conditional statement. It allows you to perform different actions (run different code) in different situations. This won’t really be used in our simulation, but it’s really useful in many other contexts. These statements look something like this:

if (some_logical_statement){
  ...do one thing...
} else if (some other logical statement){
  ...do a different thing...
} else{
  ...do a third thing...
}

These conditional statements can contain just the if part, or just the if and else parts, or any number of else if statements in the middle.

They look something like this:

best_plant <- 'moss'

if (best_plant == 'moss'){
  print("Bryophytes forever")
}else{
  print("You're mistaken")
}

Try changing the value of the best_plant variable above and re-running the code.

Importantly, these chains of statements are evaluated top to bottom, and once one part is found to be true, the rest is not run. Take a look at this example:

current_value <- 8

if (current_value > 6){
  print('Greater than 6')
} else if (current_value > 4){
  print("Greater than 4, but not 6!")
} else{
  print("Less than or equal to 4")
}

Notice that if we set current_value to 8, even though that’s both greater than 6 AND 4, only the first statement prints. This is what the else part of the else if is doing.

Simulation experiment setup

We’ve been working with the iris dataset throughout this course, so let’s stick with plants for this simulation! Let’s think about an experiment in which we try to test the effect of salt on plant growth. This will allow us to explore some of the key programming concepts we want to learn.

Using variables to set up simulations

Let’s start simple, and try to just perform a very simple digital plant growth experiment, with no salt treatment for now. We can start our virtual plants as seeds at timepoint t0, and record their height in centimeters in a vector. Here, we need to make a key first decision: sample size!

Let’s decide our sample size is 3 for now. One (not great) way we can make our initial vector is by just writing out the starting heights.

not_great_day_0_plants <- c(0, 0, 0)

Why is this not great? Well, what if we want to start with a much larger sample size? Or, what if we want to be able to easily change the sample size in our experiment, as we’ll want to do later? Instead, we can declare the sample size as a variable, and use the rep() function, which repeats a vector (or single string/number) a specified number of times, to create this initial vector. (A note for your future programmer self: rep() can be used to repeat both individual elements in a vector and a whole vector, and can come in quite useful.)

# declare sample size
sample_size <- 3
# create a vector in which 0 is repated sample_size times
day_0_plants <- rep(0, sample_size)
print(day_0_plants)

OK, now let’s try to make our plants grow. Here, we need to decide how many centimeters our plants are growing each day!

# specify plant growth per timepoint
plant_growth_per_day <- 0.5

# create a new vector, day_1_plants, in which each of the plants grows by
# plant_growth_per_day

Simulation using randomly drawn numbers

This model, in which each plant grows identically by the same amount every day, hardly seems realistic or useful. Maybe a more realistic model is that each plant grows some amount on average, but with random noise in each plant’s growth amount. R can easily simulate randomly drawn numbers from a large number of distributions, with the format r{distribution name abbreviation}({sample_number_to_draw}, {distribution parameters}), where the ‘r’ at the beginning stands for ‘random’: for example, for random samples from a normal distribution, we can use rnorm({sample_number_to_draw}, {mean}, {standard_deviation}), for a binomial distribution rbinom({sample_number_to_draw}, {num_trials}, {trial_probability}), etc. The documentation for these functions in R is usually quite helpful.

We’ll use a Normal distribution to model growth here, even though that’s really not a good idea in general, since that can result in negative growth amounts. Thinking about what the noise in your data might realistically look like is key for both running your simulations and running statistical analysis on your data!

# specify the standard deviation of daily plant growth
plant_growth_sd <- 0.1

# create a new vector, day_2_plants, using day_1_plants, plant_growth_per_day,
# plant_growth_sd, and the rnorm() function

Let’s remind ourselves of the assumptions in our model so far:

  • linear growth
  • all seeds germinate on day 0
  • normally distributed growth rates

Saving simulation results into a dataframe

We now have two vectors, day_0_plants and day_1_plants, for the growth of plants over time. However, what if we want to track these plants over the course of a month? Do we make a new vector for each day? And, how do we handle plotting these plants’ growth, or doing statistical analysis (e.g. trying to run lm())? Clearly, it’d be better to put this data into a “tidy” dataframe.

The first step to doing this is thinking about what that would look like. Remember that in a tidy dataframe, each individual observation has its own row. But what is an observation here? Is it a single plant, with a column for each day’s height? Or is it a single plant on a single day? Remember that one thing that may help you to think about this is to imagine plotting this data on a scatterplot (e.g. plant height over time). If you’re using ggplot, each observation (row) would result in a single point on your plot.


As hopefully everyone agrees, our dataframe in this case should have a single row for our observation of each plant on each day, with a height column for that day. We should also have a day column to indicate which day the height was recorded on. Finally, it’s probably a good idea to have some sort of plant.id column, so that we can look at the growth of individual plants later on if we need.

Let’s first create a dataframe just for the day 0 data.

# Create a new dataframe, day_0_df, containing the columns described above, and
# the data for plant heights from day_0_data (hint: check class 5)
# For simplicity, make plant.id just integers from 1 to sample_size
# hint: use seq() or just a colon

We can now create a new dataframe for the day 1 data based on the day 0 dataframe

# Use day_0_df, as well as the rnorm function and parameters described above, to
# create a new dataframe, day_1_df

We can combine day_0_df and day_1_df into a single tidy dataframe by using the rbind() (row bind) function, which adds a dataframe to the bottom of another dataframe, as long as they have the same columns.

plant_growth_df <- rbind(day_0_df, day_1_df)

Writing custom functions

Above, we have the code we need to add a day of growth to an existing dataframe. Let’s create a function to do this automatically. That way, when we need to add a new day to plant_growth_df in the future, we can do this automatically in a single line, by calling our function the same way we call any other function in R.

Simple function example

Below is an example of what a function with two inputs (input_1 and input_2) and one output (output_2) would look like:

my_function_name <- function(input_1, input_2){
  # Some code that does something to input_1 and input_2 to create an
  # output_variable, output_1
  # ...
  return(output_1)
}

Notice that you’re assigning the function to a function name you make up, just like you’d assign a variable. The variable names in parentheses after function are inputs into your function; these will be used as variables inside the function code. Then you open a curly brace {, write out the code of your function, and end it with a return(). Although functions in R will work without a return(), their behavior will be unclear and maybe difficult to predict, so I really suggest not forgetting to include this line (once!) at the end of every function.

Also, note that the code inside your function can work just like any other code: you can declare variables, run other functions (either built-in, or ones you’ve already declared earlier), etc. The caveat, though, is that the only variables you should assume the function has access to at the start are the input variables specified in your parentheses. Think of a function as a completely closed box that can only communicate with the rest of your code via the input(s) you pass into it, and the output(s) it returns.

Here’s a function that finds the hypotenuse of a right triangle. It needs to take two inputs and add their squares, and then return the square root of the result.

# create a function that calculates the length of the hypotenuse of a right
# triangle
pythagorean_theorem_fun <- function(side_1, side_2){
  # square each of the triangle sides and add them
  hypotenuse_squared <- side_1^2 + side_2^2
  # take the square root of the sum of squared sides
  hypotenuse <- sqrt(hypotenuse_squared)
  # don't forget to tell R which of the variables created inside this function
  # you want it to output!
  return(hypotenuse)
}

# Try running the function above on some test numbers, just as you would any
# other R function

Notice that my function contains a ton of comments, explaining exactly what each of the steps does. Doing this will get you a big, big thank you from your future self!

One really key thing to remember, especially in R, is to be careful about naming variables inside this function. You want to avoid declaring a function in which some variables are called the same thing as variables you already have loaded into your workspace: this could cause the source of any errors in your function to be incredibly difficult to track down. So, be creative about your variable names.

Writing a function for plant growth

Now we can start putting together our plant growth function. First, we need to figure out what the inputs and outputs of our function will be. Here’s a proposal:

Inputs:

  • a dataframe containing plant heights for previous days (something that looks like plant_growth_df)
  • the ‘current day’
  • mean plant growth per day
  • standard deviation of plant growth per day

Outputs:

  • a dataframe containing plant heights, with the new day’s growth added

We’ll also need to figure out the steps we’ll need to get from the inputs to the outputs. Again, here is a proposal:

Algorithm

  1. Subset the data from the plant height dataframe, containing only the data recorded on the day before the ‘current day’
  2. Create a new dataframe for the ‘current’ day, using rnorm() and the previous day’s heights to generate new heights
  3. Combined the new dataframe with the full plant height dataframe
  4. Return the new, combined plant height dataframe using the return() function (this is how R knows what you want the output of the function to be!)

Let’s follow the template above to write a function that takes in a dataframe containing plant heights (something that looks like plant_growth_df, for example), as well as the other required inputs, as described above.

# Write a function called grow_plants that adds a day of plant growth to a
# dataframe containing plant_IDs, plant heights, and growth days

# Run grow_plants on plant_growth_df to add another day to this dataframe

Saving function scripts

Let’s save our grow_plants() function to its own separate script so we can use it again next time.

  1. Click on File -> New File -> R Script. A new tab should appear in Rstudio.

  2. Take the entire grow_plants function (including the part where you assign the function to the grow_plants variable), copy it, and paste it into the newly opened script.

  3. Click on File -> Save As, and save your function somewhere on your computer (probably best to do this in the directory where you are saving your notebooks for this course). You can name the file anything you want (it should have a .R extension), but I suggest naming files that hold functions after the functions themselves, i.e. grow_plants.R.

  4. To test that this worked, clear everything in your environment using the little broom at the top right corner of your screen. Then, run the source() command with the full path to grow_plants.R as the argument

# something like this
source('~/Documents/Teaching/Intro_R_Course/grow_plants.R')
  1. Check that the grow_plants() function has appeared in your Environment. You can try rerunning it using the code above
LS0tCnRpdGxlOiAiSW50cm8gUiBDb3Vyc2UsIFdvcmtzaG9wIDg6IEJhc2ljcyBvZiBwcm9ncmFtbWluZywgYW5kIHNpbXVsYXRpb24gaW4gYmlvbG9neSAoUGFydCBJKSIKc3VidGl0bGU6IHwKICAgIHwgICAtIGNvbmRpdGlvbmFsIHN0YXRlbWVudHMKICAgIHwgICAtIHNpbXVsYXRpbmcgYmlvbG9naWNhbCBkYXRhIChpbnRybykKICAgIHwgICAtIHJhbmRvbSBudW1iZXIgZ2VuZXJhdGlvbgogICAgfCAgIC0gd3JpdGluZyB5b3VyIG93biBmdW5jdGlvbnMKYXV0aG9yOgogIC0gRXVnZW5lIFBsYXZza2luCm91dHB1dDoKICBodG1sX25vdGVib29rOgogICAgY29kZV9mb2xkaW5nOiBzaG93CiAgICBkZXB0aDogMwogICAgdGlkeTogeWVzCiAgICB0b2M6IHllcwotLS0KKipTb2x1dGlvbnMgdG8gdGhpcyB3b3Jrc2hvcCBjYW4gYmUgZm91bmQgW2hlcmVdKFNvbHV0aW9uc19Xb3Jrc2hvcF84Lm5iLmh0bWwpKioKClRoZSB0ZWNobmlxdWVzIHdlJ3ZlIGxlYXJuZWQgaW4gdGhlIGxhc3QgZmV3IHdvcmtzaG9wcyBhcmUgZ2VhcmVkIHRvd2FyZHMgaGVscGluZyB5b3UgdW5kZXJzdGFuZCB0aGUgYmFzaWNzIG9mIGhvdyB0byBhbmFseXplIGRhdGEgaW4gUi4gSG93ZXZlciwgUiBpcyBhIGZ1bGwtZmxlZGdlZCBwcm9ncmFtbWluZyBsYW5ndWFnZSwgYW5kIHlvdSBjYW4gdXNlIGl0IHRvIGRvIGEgbG90IG1vcmUgdGhhbiBqdXN0IHBsb3QgZGF0YSBhbmQgcnVuIHN0YXRzLgoKT25lIHRlY2huaXF1ZSB0aGF0IGNhbiBiZSBpbmNyZWRpYmx5IGhlbHBmdWwgaW4gcGxhbm5pbmcgZXhwZXJpbWVudHMsIHVuZGVyc3RhbmRpbmcgZGF0YSwgYW5kIHRyeWluZyBvdXQgc3RhdGlzdGljYWwgYXBwcm9hY2hlcyBpcyAqc2ltdWxhdGlvbiouIEluIG1hbnkgY2FzZXMsIGJ5IHNpbXVsYXRpbmcgd2hhdCB3ZSBleHBlY3Qgb3VyIGRhdGEgdG8gbG9vayBsaWtlIChpZGVhbGx5IGJlZm9yZSBldmVuIHNldHRpbmcgdXAgYW55IGV4cGVyaW1lbnRzKSwgd2UgY2FuIHF1aWNrbHkgZmlndXJlIG91dCBjb25mb3VuZGluZyBmYWN0b3JzIGluIGV4cGVyaW1lbnRhbCBkZXNpZ24gd2UgaGFkIG5vdCB5ZXQgdGhvdWdodCBhYm91dDsgZXN0aW1hdGUgdGhlIHNhbXBsZSBzaXplcyB3ZSB3b3VsZCBuZWVkIHRvIGRldGVjdCBlZmZlY3RzIHRoYXQgaW50ZXJlc3QgdXMsIGlmIHRob3NlIGVmZmVjdHMgaW5kZWVkIGV4aXN0OyBhbmQgc3RhcnQgdG8gdGhpbmsgYWJvdXQgYW5kIHByb2dyYW0gdGhlIHN0YXRpc3RpY2FsIGFuYWx5c2VzIHdlJ2QgaGF2ZSB0byBwZXJmb3JtIG9uIG91ciByZWFsIGRhdGEgb25jZSB3ZSBoYXZlIGl0LgoKVGhlc2UgZmluYWwgd29ya3Nob3BzIGNvdmVyIHNvbWUgaWRlYXMgdGhhdCBhcmUgZnVuZGFtZW50YWwgdG8gcHJvZ3JhbW1pbmcgaW4gYW55IGxhbmd1YWdlIHRocm91Z2ggdGhlIGxlbnMgb2Ygc2ltdWxhdGluZyB0aGUgZXhwZWN0ZWQgcmVzdWx0cyBvZiBhbiBleHBlcmltZW50LiBXZSdsbCBhbHNvIHRyeSB0byBjb21lIGJhY2sgdG8gc29tZSBvZiB0aGUgY29uY2VwdHMgd2UndmUgYWxyZWFkeSBjb3ZlcmVkLCBidXQgYSBrZXkgcG9pbnQgaXMgdGhhdCB3aGlsZSBtb3N0IG9mIHRoZSBtYXRlcmlhbCBpbiB3b3Jrc2hvcHMgNS03IGlzIHF1aXRlIHNwZWNpZmljIHRvIFIsIHRoZSBpZGVhcyB3ZSdsbCBjb3ZlciBpbiB0aGlzIHdvcmtzaG9wIHdpbGwgY2Fycnkgb3ZlciAod2l0aCBzb21lIHN5bnRheCBkaWZmZXJlbmNlcykgdG8gYW55IG90aGVyIGNvbXB1dGVyIGxhbmd1YWdlIHlvdSBsZWFybi4KCiMgQ29uZGl0aW9uYWwgKElmL0Vsc2UpIFN0YXRlbWVudHMKCk9uZSB2ZXJ5IHVzZWZ1bCBjb25zdHJ1Y3QgaW4gcHJvZ3JhbW1pbmcgaXMgYSBjb25kaXRpb25hbCBzdGF0ZW1lbnQuIEl0IGFsbG93cyB5b3UgdG8gcGVyZm9ybSBkaWZmZXJlbnQgYWN0aW9ucyAocnVuIGRpZmZlcmVudCBjb2RlKSBpbiBkaWZmZXJlbnQgc2l0dWF0aW9ucy4gVGhpcyB3b24ndCByZWFsbHkgYmUgdXNlZCBpbiBvdXIgc2ltdWxhdGlvbiwgYnV0IGl0J3MgcmVhbGx5IHVzZWZ1bCBpbiBtYW55IG90aGVyIGNvbnRleHRzLiBUaGVzZSBzdGF0ZW1lbnRzIGxvb2sgc29tZXRoaW5nIGxpa2UgdGhpczoKYGBge30KaWYgKHNvbWVfbG9naWNhbF9zdGF0ZW1lbnQpewogIC4uLmRvIG9uZSB0aGluZy4uLgp9IGVsc2UgaWYgKHNvbWUgb3RoZXIgbG9naWNhbCBzdGF0ZW1lbnQpewogIC4uLmRvIGEgZGlmZmVyZW50IHRoaW5nLi4uCn0gZWxzZXsKICAuLi5kbyBhIHRoaXJkIHRoaW5nLi4uCn0KYGBgClRoZXNlIGNvbmRpdGlvbmFsIHN0YXRlbWVudHMgY2FuIGNvbnRhaW4ganVzdCB0aGUgYGlmYCBwYXJ0LCBvciBqdXN0IHRoZSBgaWZgIGFuZCBgZWxzZWAgcGFydHMsIG9yIGFueSBudW1iZXIgb2YgYGVsc2UgaWZgIHN0YXRlbWVudHMgaW4gdGhlIG1pZGRsZS4KClRoZXkgbG9vayBzb21ldGhpbmcgbGlrZSB0aGlzOgpgYGB7cn0KYmVzdF9wbGFudCA8LSAnbW9zcycKCmlmIChiZXN0X3BsYW50ID09ICdtb3NzJyl7CiAgcHJpbnQoIkJyeW9waHl0ZXMgZm9yZXZlciIpCn1lbHNlewogIHByaW50KCJZb3UncmUgbWlzdGFrZW4iKQp9CmBgYApUcnkgY2hhbmdpbmcgdGhlIHZhbHVlIG9mIHRoZSBgYmVzdF9wbGFudGAgdmFyaWFibGUgYWJvdmUgYW5kIHJlLXJ1bm5pbmcgdGhlIGNvZGUuCgpJbXBvcnRhbnRseSwgdGhlc2UgY2hhaW5zIG9mIHN0YXRlbWVudHMgYXJlIGV2YWx1YXRlZCB0b3AgdG8gYm90dG9tLCBhbmQgb25jZSBvbmUgcGFydCBpcyBmb3VuZCB0byBiZSB0cnVlLCB0aGUgcmVzdCBpcyBub3QgcnVuLiBUYWtlIGEgbG9vayBhdCB0aGlzIGV4YW1wbGU6CmBgYHtyfQpjdXJyZW50X3ZhbHVlIDwtIDgKCmlmIChjdXJyZW50X3ZhbHVlID4gNil7CiAgcHJpbnQoJ0dyZWF0ZXIgdGhhbiA2JykKfSBlbHNlIGlmIChjdXJyZW50X3ZhbHVlID4gNCl7CiAgcHJpbnQoIkdyZWF0ZXIgdGhhbiA0LCBidXQgbm90IDYhIikKfSBlbHNlewogIHByaW50KCJMZXNzIHRoYW4gb3IgZXF1YWwgdG8gNCIpCn0KYGBgCk5vdGljZSB0aGF0IGlmIHdlIHNldCBjdXJyZW50X3ZhbHVlIHRvIDgsIGV2ZW4gdGhvdWdoIHRoYXQncyBib3RoIGdyZWF0ZXIgdGhhbiA2IEFORCA0LCBvbmx5IHRoZSBmaXJzdCBzdGF0ZW1lbnQgcHJpbnRzLiBUaGlzIGlzIHdoYXQgdGhlIGBlbHNlYCBwYXJ0IG9mIHRoZSBgZWxzZSBpZmAgaXMgZG9pbmcuCgojIFNpbXVsYXRpb24gZXhwZXJpbWVudCBzZXR1cAoKV2UndmUgYmVlbiB3b3JraW5nIHdpdGggdGhlIGlyaXMgZGF0YXNldCB0aHJvdWdob3V0IHRoaXMgY291cnNlLCBzbyBsZXQncyBzdGljayB3aXRoIHBsYW50cyBmb3IgdGhpcyBzaW11bGF0aW9uISBMZXQncyB0aGluayBhYm91dCBhbiBleHBlcmltZW50IGluIHdoaWNoIHdlIHRyeSB0byB0ZXN0IHRoZSBlZmZlY3Qgb2Ygc2FsdCBvbiBwbGFudCBncm93dGguIFRoaXMgd2lsbCBhbGxvdyB1cyB0byBleHBsb3JlIHNvbWUgb2YgdGhlIGtleSBwcm9ncmFtbWluZyBjb25jZXB0cyB3ZSB3YW50IHRvIGxlYXJuLgoKIyMgVXNpbmcgdmFyaWFibGVzIHRvIHNldCB1cCBzaW11bGF0aW9ucwoKTGV0J3Mgc3RhcnQgc2ltcGxlLCBhbmQgdHJ5IHRvIGp1c3QgcGVyZm9ybSBhIHZlcnkgc2ltcGxlIGRpZ2l0YWwgcGxhbnQgZ3Jvd3RoIGV4cGVyaW1lbnQsIHdpdGggbm8gc2FsdCB0cmVhdG1lbnQgZm9yIG5vdy4gV2UgY2FuIHN0YXJ0IG91ciB2aXJ0dWFsIHBsYW50cyBhcyBzZWVkcyBhdCB0aW1lcG9pbnQgKnQwKiwgYW5kIHJlY29yZCB0aGVpciBoZWlnaHQgaW4gY2VudGltZXRlcnMgaW4gYSB2ZWN0b3IuIEhlcmUsIHdlIG5lZWQgdG8gbWFrZSBhIGtleSBmaXJzdCBkZWNpc2lvbjogc2FtcGxlIHNpemUhCgpMZXQncyBkZWNpZGUgb3VyIHNhbXBsZSBzaXplIGlzIDMgZm9yIG5vdy4gT25lIChub3QgZ3JlYXQpIHdheSB3ZSBjYW4gbWFrZSBvdXIgaW5pdGlhbCB2ZWN0b3IgaXMgYnkganVzdCB3cml0aW5nIG91dCB0aGUgc3RhcnRpbmcgaGVpZ2h0cy4KYGBge3J9Cm5vdF9ncmVhdF9kYXlfMF9wbGFudHMgPC0gYygwLCAwLCAwKQpgYGAKCldoeSBpcyB0aGlzIG5vdCBncmVhdD8gV2VsbCwgd2hhdCBpZiB3ZSB3YW50IHRvIHN0YXJ0IHdpdGggYSAqbXVjaCogbGFyZ2VyIHNhbXBsZSBzaXplPyBPciwgd2hhdCBpZiB3ZSB3YW50IHRvIGJlIGFibGUgdG8gZWFzaWx5IGNoYW5nZSB0aGUgc2FtcGxlIHNpemUgaW4gb3VyIGV4cGVyaW1lbnQsIGFzIHdlJ2xsIHdhbnQgdG8gZG8gbGF0ZXI/IEluc3RlYWQsIHdlIGNhbiBkZWNsYXJlIHRoZSBzYW1wbGUgc2l6ZSBhcyBhIHZhcmlhYmxlLCBhbmQgdXNlIHRoZSBgcmVwKClgIGZ1bmN0aW9uLCB3aGljaCByZXBlYXRzIGEgdmVjdG9yIChvciBzaW5nbGUgc3RyaW5nL251bWJlcikgYSBzcGVjaWZpZWQgbnVtYmVyIG9mIHRpbWVzLCB0byBjcmVhdGUgdGhpcyBpbml0aWFsIHZlY3Rvci4gKEEgbm90ZSBmb3IgeW91ciBmdXR1cmUgcHJvZ3JhbW1lciBzZWxmOiBgcmVwKClgIGNhbiBiZSB1c2VkIHRvIHJlcGVhdCBib3RoIGluZGl2aWR1YWwgZWxlbWVudHMgaW4gYSB2ZWN0b3IgYW5kIGEgd2hvbGUgdmVjdG9yLCBhbmQgY2FuIGNvbWUgaW4gcXVpdGUgdXNlZnVsLikKYGBge3J9CiMgZGVjbGFyZSBzYW1wbGUgc2l6ZQpzYW1wbGVfc2l6ZSA8LSAzCiMgY3JlYXRlIGEgdmVjdG9yIGluIHdoaWNoIDAgaXMgcmVwYXRlZCBzYW1wbGVfc2l6ZSB0aW1lcwpkYXlfMF9wbGFudHMgPC0gcmVwKDAsIHNhbXBsZV9zaXplKQpwcmludChkYXlfMF9wbGFudHMpCmBgYAoKT0ssIG5vdyBsZXQncyB0cnkgdG8gbWFrZSBvdXIgcGxhbnRzIGdyb3cuIEhlcmUsIHdlIG5lZWQgdG8gZGVjaWRlIGhvdyBtYW55IGNlbnRpbWV0ZXJzIG91ciBwbGFudHMgYXJlIGdyb3dpbmcgZWFjaCBkYXkhCmBgYHtyfQojIHNwZWNpZnkgcGxhbnQgZ3Jvd3RoIHBlciB0aW1lcG9pbnQKcGxhbnRfZ3Jvd3RoX3Blcl9kYXkgPC0gMC41CgojIGNyZWF0ZSBhIG5ldyB2ZWN0b3IsIGRheV8xX3BsYW50cywgaW4gd2hpY2ggZWFjaCBvZiB0aGUgcGxhbnRzIGdyb3dzIGJ5CiMgcGxhbnRfZ3Jvd3RoX3Blcl9kYXkKCmBgYAoKIyMgU2ltdWxhdGlvbiB1c2luZyByYW5kb21seSBkcmF3biBudW1iZXJzCgpUaGlzIG1vZGVsLCBpbiB3aGljaCBlYWNoIHBsYW50IGdyb3dzIGlkZW50aWNhbGx5IGJ5IHRoZSBzYW1lIGFtb3VudCBldmVyeSBkYXksIGhhcmRseSBzZWVtcyByZWFsaXN0aWMgb3IgdXNlZnVsLiBNYXliZSBhIG1vcmUgcmVhbGlzdGljIG1vZGVsIGlzIHRoYXQgZWFjaCBwbGFudCBncm93cyBzb21lIGFtb3VudCBvbiBhdmVyYWdlLCBidXQgd2l0aCByYW5kb20gbm9pc2UgaW4gZWFjaCBwbGFudCdzIGdyb3d0aCBhbW91bnQuIFIgY2FuIGVhc2lseSBzaW11bGF0ZSByYW5kb21seSBkcmF3biBudW1iZXJzIGZyb20gYSBsYXJnZSBudW1iZXIgb2YgZGlzdHJpYnV0aW9ucywgd2l0aCB0aGUgZm9ybWF0IGBye2Rpc3RyaWJ1dGlvbiBuYW1lIGFiYnJldmlhdGlvbn0oe3NhbXBsZV9udW1iZXJfdG9fZHJhd30sIHtkaXN0cmlidXRpb24gcGFyYW1ldGVyc30pYCwgd2hlcmUgdGhlICdyJyBhdCB0aGUgYmVnaW5uaW5nIHN0YW5kcyBmb3IgJ3JhbmRvbSc6IGZvciBleGFtcGxlLCBmb3IgcmFuZG9tIHNhbXBsZXMgZnJvbSBhIG5vcm1hbCBkaXN0cmlidXRpb24sIHdlIGNhbiB1c2UgYHJub3JtKHtzYW1wbGVfbnVtYmVyX3RvX2RyYXd9LCB7bWVhbn0sIHtzdGFuZGFyZF9kZXZpYXRpb259KWAsIGZvciBhIGJpbm9taWFsIGRpc3RyaWJ1dGlvbiBgcmJpbm9tKHtzYW1wbGVfbnVtYmVyX3RvX2RyYXd9LCB7bnVtX3RyaWFsc30sIHt0cmlhbF9wcm9iYWJpbGl0eX0pYCwgZXRjLiBUaGUgZG9jdW1lbnRhdGlvbiBmb3IgdGhlc2UgZnVuY3Rpb25zIGluIFIgaXMgdXN1YWxseSBxdWl0ZSBoZWxwZnVsLgoKV2UnbGwgdXNlIGEgTm9ybWFsIGRpc3RyaWJ1dGlvbiB0byBtb2RlbCBncm93dGggaGVyZSwgZXZlbiB0aG91Z2ggdGhhdCdzIHJlYWxseSBub3QgYSBnb29kIGlkZWEgaW4gZ2VuZXJhbCwgc2luY2UgdGhhdCBjYW4gcmVzdWx0IGluIG5lZ2F0aXZlIGdyb3d0aCBhbW91bnRzLiAqKlRoaW5raW5nIGFib3V0IHdoYXQgdGhlIG5vaXNlIGluIHlvdXIgZGF0YSBtaWdodCByZWFsaXN0aWNhbGx5IGxvb2sgbGlrZSBpcyBrZXkgZm9yIGJvdGggcnVubmluZyB5b3VyIHNpbXVsYXRpb25zIGFuZCBydW5uaW5nIHN0YXRpc3RpY2FsIGFuYWx5c2lzIG9uIHlvdXIgZGF0YSEqKgoKYGBge3J9CiMgc3BlY2lmeSB0aGUgc3RhbmRhcmQgZGV2aWF0aW9uIG9mIGRhaWx5IHBsYW50IGdyb3d0aApwbGFudF9ncm93dGhfc2QgPC0gMC4xCgojIGNyZWF0ZSBhIG5ldyB2ZWN0b3IsIGRheV8yX3BsYW50cywgdXNpbmcgZGF5XzFfcGxhbnRzLCBwbGFudF9ncm93dGhfcGVyX2RheSwKIyBwbGFudF9ncm93dGhfc2QsIGFuZCB0aGUgcm5vcm0oKSBmdW5jdGlvbgoKYGBgCgoqKkxldCdzIHJlbWluZCBvdXJzZWx2ZXMgb2YgdGhlIGFzc3VtcHRpb25zIGluIG91ciBtb2RlbCBzbyBmYXI6KioKCiogbGluZWFyIGdyb3d0aAoqIGFsbCBzZWVkcyBnZXJtaW5hdGUgb24gZGF5IDAKKiBub3JtYWxseSBkaXN0cmlidXRlZCBncm93dGggcmF0ZXMKCiMjIFNhdmluZyBzaW11bGF0aW9uIHJlc3VsdHMgaW50byBhIGRhdGFmcmFtZQoKV2Ugbm93IGhhdmUgdHdvIHZlY3RvcnMsIGBkYXlfMF9wbGFudHNgIGFuZCBgZGF5XzFfcGxhbnRzYCwgZm9yIHRoZSBncm93dGggb2YgcGxhbnRzIG92ZXIgdGltZS4gSG93ZXZlciwgd2hhdCBpZiB3ZSB3YW50IHRvIHRyYWNrIHRoZXNlIHBsYW50cyBvdmVyIHRoZSBjb3Vyc2Ugb2YgYSBtb250aD8gRG8gd2UgbWFrZSBhIG5ldyB2ZWN0b3IgZm9yIGVhY2ggZGF5PyBBbmQsIGhvdyBkbyB3ZSBoYW5kbGUgcGxvdHRpbmcgdGhlc2UgcGxhbnRzJyBncm93dGgsIG9yIGRvaW5nIHN0YXRpc3RpY2FsIGFuYWx5c2lzIChlLmcuIHRyeWluZyB0byBydW4gYGxtKClgKT8gQ2xlYXJseSwgaXQnZCBiZSBiZXR0ZXIgdG8gcHV0IHRoaXMgZGF0YSBpbnRvIGEgInRpZHkiIGRhdGFmcmFtZS4KClRoZSBmaXJzdCBzdGVwIHRvIGRvaW5nIHRoaXMgaXMgdGhpbmtpbmcgYWJvdXQgd2hhdCB0aGF0IHdvdWxkIGxvb2sgbGlrZS4gUmVtZW1iZXIgdGhhdCBpbiBhIHRpZHkgZGF0YWZyYW1lLCBlYWNoIGluZGl2aWR1YWwgb2JzZXJ2YXRpb24gaGFzIGl0cyBvd24gcm93LiBCdXQgd2hhdCBpcyBhbiBvYnNlcnZhdGlvbiBoZXJlPyBJcyBpdCBhIHNpbmdsZSBwbGFudCwgd2l0aCBhIGNvbHVtbiBmb3IgZWFjaCBkYXkncyBoZWlnaHQ/IE9yIGlzIGl0IGEgc2luZ2xlIHBsYW50IG9uIGEgc2luZ2xlIGRheT8gUmVtZW1iZXIgdGhhdCBvbmUgdGhpbmcgdGhhdCBtYXkgaGVscCB5b3UgdG8gdGhpbmsgYWJvdXQgdGhpcyBpcyB0byBpbWFnaW5lIHBsb3R0aW5nIHRoaXMgZGF0YSBvbiBhIHNjYXR0ZXJwbG90IChlLmcuIHBsYW50IGhlaWdodCBvdmVyIHRpbWUpLiBJZiB5b3UncmUgdXNpbmcgZ2dwbG90LCBlYWNoIG9ic2VydmF0aW9uIChyb3cpIHdvdWxkIHJlc3VsdCBpbiBhIHNpbmdsZSBwb2ludCBvbiB5b3VyIHBsb3QuCgoqKioKCkFzIGhvcGVmdWxseSBldmVyeW9uZSBhZ3JlZXMsIG91ciBkYXRhZnJhbWUgaW4gdGhpcyBjYXNlIHNob3VsZCBoYXZlIGEgc2luZ2xlIHJvdyBmb3Igb3VyIG9ic2VydmF0aW9uIG9mIGVhY2ggcGxhbnQgb24gZWFjaCBkYXksIHdpdGggYSBgaGVpZ2h0YCBjb2x1bW4gZm9yIHRoYXQgZGF5LiBXZSBzaG91bGQgYWxzbyBoYXZlIGEgYGRheWAgY29sdW1uIHRvIGluZGljYXRlIHdoaWNoIGRheSB0aGUgaGVpZ2h0IHdhcyByZWNvcmRlZCBvbi4gRmluYWxseSwgaXQncyBwcm9iYWJseSBhIGdvb2QgaWRlYSB0byBoYXZlIHNvbWUgc29ydCBvZiBgcGxhbnQuaWRgIGNvbHVtbiwgc28gdGhhdCB3ZSBjYW4gbG9vayBhdCB0aGUgZ3Jvd3RoIG9mIGluZGl2aWR1YWwgcGxhbnRzIGxhdGVyIG9uIGlmIHdlIG5lZWQuCgpMZXQncyBmaXJzdCBjcmVhdGUgYSBkYXRhZnJhbWUganVzdCBmb3IgdGhlIGRheSAwIGRhdGEuCmBgYHtyfQojIENyZWF0ZSBhIG5ldyBkYXRhZnJhbWUsIGRheV8wX2RmLCBjb250YWluaW5nIHRoZSBjb2x1bW5zIGRlc2NyaWJlZCBhYm92ZSwgYW5kCiMgdGhlIGRhdGEgZm9yIHBsYW50IGhlaWdodHMgZnJvbSBkYXlfMF9kYXRhIChoaW50OiBjaGVjayBjbGFzcyA1KQojIEZvciBzaW1wbGljaXR5LCBtYWtlIHBsYW50LmlkIGp1c3QgaW50ZWdlcnMgZnJvbSAxIHRvIHNhbXBsZV9zaXplCiMgaGludDogdXNlIHNlcSgpIG9yIGp1c3QgYSBjb2xvbgoKYGBgCgpXZSBjYW4gbm93IGNyZWF0ZSBhIG5ldyBkYXRhZnJhbWUgZm9yIHRoZSBkYXkgMSBkYXRhIGJhc2VkIG9uIHRoZSBkYXkgMCBkYXRhZnJhbWUKYGBge3J9CiMgVXNlIGRheV8wX2RmLCBhcyB3ZWxsIGFzIHRoZSBybm9ybSBmdW5jdGlvbiBhbmQgcGFyYW1ldGVycyBkZXNjcmliZWQgYWJvdmUsIHRvCiMgY3JlYXRlIGEgbmV3IGRhdGFmcmFtZSwgZGF5XzFfZGYKCmBgYAoKV2UgY2FuIGNvbWJpbmUgYGRheV8wX2RmYCBhbmQgYGRheV8xX2RmYCBpbnRvIGEgc2luZ2xlIHRpZHkgZGF0YWZyYW1lIGJ5IHVzaW5nIHRoZSBgcmJpbmQoKWAgKHJvdyBiaW5kKSBmdW5jdGlvbiwgd2hpY2ggYWRkcyBhIGRhdGFmcmFtZSB0byB0aGUgYm90dG9tIG9mIGFub3RoZXIgZGF0YWZyYW1lLCBhcyBsb25nIGFzIHRoZXkgaGF2ZSB0aGUgc2FtZSBjb2x1bW5zLgpgYGB7cn0KcGxhbnRfZ3Jvd3RoX2RmIDwtIHJiaW5kKGRheV8wX2RmLCBkYXlfMV9kZikKYGBgCgojIFdyaXRpbmcgY3VzdG9tIGZ1bmN0aW9ucwoKQWJvdmUsIHdlIGhhdmUgdGhlIGNvZGUgd2UgbmVlZCB0byBhZGQgYSBkYXkgb2YgZ3Jvd3RoIHRvIGFuIGV4aXN0aW5nIGRhdGFmcmFtZS4gTGV0J3MgY3JlYXRlIGEgZnVuY3Rpb24gdG8gZG8gdGhpcyBhdXRvbWF0aWNhbGx5LiBUaGF0IHdheSwgd2hlbiB3ZSBuZWVkIHRvIGFkZCBhIG5ldyBkYXkgdG8gcGxhbnRfZ3Jvd3RoX2RmIGluIHRoZSBmdXR1cmUsIHdlIGNhbiBkbyB0aGlzIGF1dG9tYXRpY2FsbHkgaW4gYSBzaW5nbGUgbGluZSwgYnkgY2FsbGluZyBvdXIgZnVuY3Rpb24gdGhlIHNhbWUgd2F5IHdlIGNhbGwgYW55IG90aGVyIGZ1bmN0aW9uIGluIFIuCgojIyBTaW1wbGUgZnVuY3Rpb24gZXhhbXBsZQoKQmVsb3cgaXMgYW4gZXhhbXBsZSBvZiB3aGF0IGEgZnVuY3Rpb24gd2l0aCB0d28gaW5wdXRzIChgaW5wdXRfMWAgYW5kIGBpbnB1dF8yYCkgYW5kIG9uZSBvdXRwdXQgKGBvdXRwdXRfMmApIHdvdWxkIGxvb2sgbGlrZToKYGBge30KbXlfZnVuY3Rpb25fbmFtZSA8LSBmdW5jdGlvbihpbnB1dF8xLCBpbnB1dF8yKXsKICAjIFNvbWUgY29kZSB0aGF0IGRvZXMgc29tZXRoaW5nIHRvIGlucHV0XzEgYW5kIGlucHV0XzIgdG8gY3JlYXRlIGFuCiAgIyBvdXRwdXRfdmFyaWFibGUsIG91dHB1dF8xCiAgIyAuLi4KICByZXR1cm4ob3V0cHV0XzEpCn0KYGBgCk5vdGljZSB0aGF0IHlvdSdyZSBhc3NpZ25pbmcgdGhlIGZ1bmN0aW9uIHRvIGEgZnVuY3Rpb24gbmFtZSB5b3UgbWFrZSB1cCwganVzdCBsaWtlIHlvdSdkIGFzc2lnbiBhIHZhcmlhYmxlLiBUaGUgdmFyaWFibGUgbmFtZXMgaW4gcGFyZW50aGVzZXMgYWZ0ZXIgYGZ1bmN0aW9uYCBhcmUgaW5wdXRzIGludG8geW91ciBmdW5jdGlvbjsgdGhlc2Ugd2lsbCBiZSB1c2VkIGFzIHZhcmlhYmxlcyBpbnNpZGUgdGhlIGZ1bmN0aW9uIGNvZGUuIFRoZW4geW91IG9wZW4gYSBjdXJseSBicmFjZSBge2AsIHdyaXRlIG91dCB0aGUgY29kZSBvZiB5b3VyIGZ1bmN0aW9uLCBhbmQgZW5kIGl0IHdpdGggYSBgcmV0dXJuKClgLiBBbHRob3VnaCBmdW5jdGlvbnMgaW4gUiB3aWxsIHdvcmsgd2l0aG91dCBhIGByZXR1cm4oKWAsIHRoZWlyIGJlaGF2aW9yIHdpbGwgYmUgdW5jbGVhciBhbmQgbWF5YmUgZGlmZmljdWx0IHRvIHByZWRpY3QsIHNvIEkgcmVhbGx5IHN1Z2dlc3Qgbm90IGZvcmdldHRpbmcgdG8gaW5jbHVkZSB0aGlzIGxpbmUgKG9uY2UhKSBhdCB0aGUgZW5kIG9mIGV2ZXJ5IGZ1bmN0aW9uLgoKQWxzbywgbm90ZSB0aGF0IHRoZSBjb2RlIGluc2lkZSB5b3VyIGZ1bmN0aW9uIGNhbiB3b3JrIGp1c3QgbGlrZSBhbnkgb3RoZXIgY29kZTogeW91IGNhbiBkZWNsYXJlIHZhcmlhYmxlcywgcnVuIG90aGVyIGZ1bmN0aW9ucyAoZWl0aGVyIGJ1aWx0LWluLCBvciBvbmVzIHlvdSd2ZSBhbHJlYWR5IGRlY2xhcmVkIGVhcmxpZXIpLCBldGMuIFRoZSBjYXZlYXQsIHRob3VnaCwgaXMgdGhhdCB0aGUgb25seSB2YXJpYWJsZXMgeW91IHNob3VsZCBhc3N1bWUgdGhlIGZ1bmN0aW9uIGhhcyBhY2Nlc3MgdG8gYXQgdGhlIHN0YXJ0IGFyZSB0aGUgaW5wdXQgdmFyaWFibGVzIHNwZWNpZmllZCBpbiB5b3VyIHBhcmVudGhlc2VzLiAqKlRoaW5rIG9mIGEgZnVuY3Rpb24gYXMgYSBjb21wbGV0ZWx5IGNsb3NlZCBib3ggdGhhdCBjYW4gb25seSBjb21tdW5pY2F0ZSB3aXRoIHRoZSByZXN0IG9mIHlvdXIgY29kZSB2aWEgdGhlIGlucHV0KHMpIHlvdSBwYXNzIGludG8gaXQsIGFuZCB0aGUgb3V0cHV0KHMpIGl0IHJldHVybnMuKioKCkhlcmUncyBhIGZ1bmN0aW9uIHRoYXQgZmluZHMgdGhlIGh5cG90ZW51c2Ugb2YgYSByaWdodCB0cmlhbmdsZS4gSXQgbmVlZHMgdG8gdGFrZSB0d28gaW5wdXRzIGFuZCBhZGQgdGhlaXIgc3F1YXJlcywgYW5kIHRoZW4gcmV0dXJuIHRoZSBzcXVhcmUgcm9vdCBvZiB0aGUgcmVzdWx0LgpgYGB7cn0KIyBjcmVhdGUgYSBmdW5jdGlvbiB0aGF0IGNhbGN1bGF0ZXMgdGhlIGxlbmd0aCBvZiB0aGUgaHlwb3RlbnVzZSBvZiBhIHJpZ2h0CiMgdHJpYW5nbGUKcHl0aGFnb3JlYW5fdGhlb3JlbV9mdW4gPC0gZnVuY3Rpb24oc2lkZV8xLCBzaWRlXzIpewogICMgc3F1YXJlIGVhY2ggb2YgdGhlIHRyaWFuZ2xlIHNpZGVzIGFuZCBhZGQgdGhlbQogIGh5cG90ZW51c2Vfc3F1YXJlZCA8LSBzaWRlXzFeMiArIHNpZGVfMl4yCiAgIyB0YWtlIHRoZSBzcXVhcmUgcm9vdCBvZiB0aGUgc3VtIG9mIHNxdWFyZWQgc2lkZXMKICBoeXBvdGVudXNlIDwtIHNxcnQoaHlwb3RlbnVzZV9zcXVhcmVkKQogICMgZG9uJ3QgZm9yZ2V0IHRvIHRlbGwgUiB3aGljaCBvZiB0aGUgdmFyaWFibGVzIGNyZWF0ZWQgaW5zaWRlIHRoaXMgZnVuY3Rpb24KICAjIHlvdSB3YW50IGl0IHRvIG91dHB1dCEKICByZXR1cm4oaHlwb3RlbnVzZSkKfQoKIyBUcnkgcnVubmluZyB0aGUgZnVuY3Rpb24gYWJvdmUgb24gc29tZSB0ZXN0IG51bWJlcnMsIGp1c3QgYXMgeW91IHdvdWxkIGFueQojIG90aGVyIFIgZnVuY3Rpb24KCmBgYApOb3RpY2UgdGhhdCBteSBmdW5jdGlvbiBjb250YWlucyBhIHRvbiBvZiBjb21tZW50cywgZXhwbGFpbmluZyBleGFjdGx5IHdoYXQgZWFjaCBvZiB0aGUgc3RlcHMgZG9lcy4gRG9pbmcgdGhpcyB3aWxsIGdldCB5b3UgYSBiaWcsIGJpZyB0aGFuayB5b3UgZnJvbSB5b3VyIGZ1dHVyZSBzZWxmIQoKT25lIHJlYWxseSBrZXkgdGhpbmcgdG8gcmVtZW1iZXIsIGVzcGVjaWFsbHkgaW4gUiwgaXMgdG8gYmUgY2FyZWZ1bCBhYm91dCBuYW1pbmcgdmFyaWFibGVzIGluc2lkZSB0aGlzIGZ1bmN0aW9uLiAqKllvdSB3YW50IHRvIGF2b2lkIGRlY2xhcmluZyBhIGZ1bmN0aW9uIGluIHdoaWNoIHNvbWUgdmFyaWFibGVzIGFyZSBjYWxsZWQgdGhlIHNhbWUgdGhpbmcgYXMgdmFyaWFibGVzIHlvdSBhbHJlYWR5IGhhdmUgbG9hZGVkIGludG8geW91ciB3b3Jrc3BhY2UqKjogdGhpcyBjb3VsZCBjYXVzZSB0aGUgc291cmNlIG9mIGFueSBlcnJvcnMgaW4geW91ciBmdW5jdGlvbiB0byBiZSBpbmNyZWRpYmx5IGRpZmZpY3VsdCB0byB0cmFjayBkb3duLiBTbywgYmUgY3JlYXRpdmUgYWJvdXQgeW91ciB2YXJpYWJsZSBuYW1lcy4KCiMjIFdyaXRpbmcgYSBmdW5jdGlvbiBmb3IgcGxhbnQgZ3Jvd3RoCgpOb3cgd2UgY2FuIHN0YXJ0IHB1dHRpbmcgdG9nZXRoZXIgb3VyIHBsYW50IGdyb3d0aCBmdW5jdGlvbi4gRmlyc3QsIHdlIG5lZWQgdG8gZmlndXJlIG91dCB3aGF0IHRoZSBpbnB1dHMgYW5kIG91dHB1dHMgb2Ygb3VyIGZ1bmN0aW9uIHdpbGwgYmUuIEhlcmUncyBhIHByb3Bvc2FsOgoKKipJbnB1dHM6KioKCiogYSBkYXRhZnJhbWUgY29udGFpbmluZyBwbGFudCBoZWlnaHRzIGZvciBwcmV2aW91cyBkYXlzIChzb21ldGhpbmcgdGhhdCBsb29rcyBsaWtlIGBwbGFudF9ncm93dGhfZGZgKQoqIHRoZSAnY3VycmVudCBkYXknCiogbWVhbiBwbGFudCBncm93dGggcGVyIGRheQoqIHN0YW5kYXJkIGRldmlhdGlvbiBvZiBwbGFudCBncm93dGggcGVyIGRheQoKKipPdXRwdXRzOioqCgoqIGEgZGF0YWZyYW1lIGNvbnRhaW5pbmcgcGxhbnQgaGVpZ2h0cywgd2l0aCB0aGUgbmV3IGRheSdzIGdyb3d0aCBhZGRlZAoKV2UnbGwgYWxzbyBuZWVkIHRvIGZpZ3VyZSBvdXQgdGhlIHN0ZXBzIHdlJ2xsIG5lZWQgdG8gZ2V0IGZyb20gdGhlIGlucHV0cyB0byB0aGUgb3V0cHV0cy4gQWdhaW4sIGhlcmUgaXMgYSBwcm9wb3NhbDoKCioqQWxnb3JpdGhtKioKCjEuIFN1YnNldCB0aGUgZGF0YSBmcm9tIHRoZSBwbGFudCBoZWlnaHQgZGF0YWZyYW1lLCBjb250YWluaW5nIG9ubHkgdGhlIGRhdGEgcmVjb3JkZWQgb24gIHRoZSBkYXkgYmVmb3JlIHRoZSAnY3VycmVudCBkYXknCjIuIENyZWF0ZSBhIG5ldyBkYXRhZnJhbWUgZm9yIHRoZSAnY3VycmVudCcgZGF5LCB1c2luZyBgcm5vcm0oKWAgYW5kIHRoZSBwcmV2aW91cyBkYXkncyBoZWlnaHRzIHRvIGdlbmVyYXRlIG5ldyBoZWlnaHRzCjMuIENvbWJpbmVkIHRoZSBuZXcgZGF0YWZyYW1lIHdpdGggdGhlIGZ1bGwgcGxhbnQgaGVpZ2h0IGRhdGFmcmFtZQo0LiAqUmV0dXJuIHRoZSBuZXcsIGNvbWJpbmVkIHBsYW50IGhlaWdodCBkYXRhZnJhbWUqIHVzaW5nIHRoZSBgcmV0dXJuKClgIGZ1bmN0aW9uICh0aGlzIGlzIGhvdyBSIGtub3dzIHdoYXQgeW91IHdhbnQgdGhlIG91dHB1dCBvZiB0aGUgZnVuY3Rpb24gdG8gYmUhKQoKTGV0J3MgZm9sbG93IHRoZSB0ZW1wbGF0ZSBhYm92ZSB0byB3cml0ZSBhIGZ1bmN0aW9uIHRoYXQgdGFrZXMgaW4gYSBkYXRhZnJhbWUgY29udGFpbmluZyBwbGFudCBoZWlnaHRzIChzb21ldGhpbmcgdGhhdCBsb29rcyBsaWtlIHBsYW50X2dyb3d0aF9kZiwgZm9yIGV4YW1wbGUpLCBhcyB3ZWxsIGFzIHRoZSBvdGhlciByZXF1aXJlZCBpbnB1dHMsIGFzIGRlc2NyaWJlZCBhYm92ZS4KYGBge3J9CiMgV3JpdGUgYSBmdW5jdGlvbiBjYWxsZWQgZ3Jvd19wbGFudHMgdGhhdCBhZGRzIGEgZGF5IG9mIHBsYW50IGdyb3d0aCB0byBhCiMgZGF0YWZyYW1lIGNvbnRhaW5pbmcgcGxhbnRfSURzLCBwbGFudCBoZWlnaHRzLCBhbmQgZ3Jvd3RoIGRheXMKCiMgUnVuIGdyb3dfcGxhbnRzIG9uIHBsYW50X2dyb3d0aF9kZiB0byBhZGQgYW5vdGhlciBkYXkgdG8gdGhpcyBkYXRhZnJhbWUKCmBgYAoKIyMgU2F2aW5nIGZ1bmN0aW9uIHNjcmlwdHMKCkxldCdzIHNhdmUgb3VyIGBncm93X3BsYW50cygpYCBmdW5jdGlvbiB0byBpdHMgb3duIHNlcGFyYXRlIHNjcmlwdCBzbyB3ZSBjYW4gdXNlIGl0IGFnYWluIG5leHQgdGltZS4KCjEuIENsaWNrIG9uIEZpbGUgLT4gTmV3IEZpbGUgLT4gUiBTY3JpcHQuIEEgbmV3IHRhYiBzaG91bGQgYXBwZWFyIGluIFJzdHVkaW8uCgoyLiBUYWtlIHRoZSBlbnRpcmUgZ3Jvd19wbGFudHMgZnVuY3Rpb24gKGluY2x1ZGluZyB0aGUgcGFydCB3aGVyZSB5b3UgYXNzaWduIHRoZSBmdW5jdGlvbiB0byB0aGUgYGdyb3dfcGxhbnRzYCB2YXJpYWJsZSksIGNvcHkgaXQsIGFuZCBwYXN0ZSBpdCBpbnRvIHRoZSBuZXdseSBvcGVuZWQgc2NyaXB0LgoKMy4gQ2xpY2sgb24gRmlsZSAtPiBTYXZlIEFzLCBhbmQgc2F2ZSB5b3VyIGZ1bmN0aW9uIHNvbWV3aGVyZSBvbiB5b3VyIGNvbXB1dGVyIChwcm9iYWJseSBiZXN0IHRvIGRvIHRoaXMgaW4gdGhlIGRpcmVjdG9yeSB3aGVyZSB5b3UgYXJlIHNhdmluZyB5b3VyIG5vdGVib29rcyBmb3IgdGhpcyBjb3Vyc2UpLiBZb3UgY2FuIG5hbWUgdGhlIGZpbGUgYW55dGhpbmcgeW91IHdhbnQgKGl0IHNob3VsZCBoYXZlIGEgKi5SKiBleHRlbnNpb24pLCBidXQgSSBzdWdnZXN0IG5hbWluZyBmaWxlcyB0aGF0IGhvbGQgZnVuY3Rpb25zIGFmdGVyIHRoZSBmdW5jdGlvbnMgdGhlbXNlbHZlcywgaS5lLiAqZ3Jvd19wbGFudHMuUiouCgo0LiBUbyB0ZXN0IHRoYXQgdGhpcyB3b3JrZWQsIGNsZWFyIGV2ZXJ5dGhpbmcgaW4geW91ciBlbnZpcm9ubWVudCB1c2luZyB0aGUgbGl0dGxlIGJyb29tIGF0IHRoZSB0b3AgcmlnaHQgY29ybmVyIG9mIHlvdXIgc2NyZWVuLiBUaGVuLCBydW4gdGhlIGBzb3VyY2UoKWAgY29tbWFuZCB3aXRoIHRoZSBmdWxsIHBhdGggdG8gKmdyb3dfcGxhbnRzLlIqIGFzIHRoZSBhcmd1bWVudApgYGB7cn0KIyBzb21ldGhpbmcgbGlrZSB0aGlzCnNvdXJjZSgnfi9Eb2N1bWVudHMvVGVhY2hpbmcvSW50cm9fUl9Db3Vyc2UvZ3Jvd19wbGFudHMuUicpCmBgYAoKNS4gQ2hlY2sgdGhhdCB0aGUgYGdyb3dfcGxhbnRzKClgIGZ1bmN0aW9uIGhhcyBhcHBlYXJlZCBpbiB5b3VyIEVudmlyb25tZW50LiBZb3UgY2FuIHRyeSByZXJ1bm5pbmcgaXQgdXNpbmcgdGhlIGNvZGUgYWJvdmUKCgoKCgo=