Introduction

In previous sections you’ve seen methods for describing individual categorical variables. Now we’ll switch over to describing numerical variables.

The most basic, and probably most commonly used way to describe the typical person in a population with respect to some characteristic that is recorded as a numerical variable, like height or weight, is with a measure of central tendency. In this lecture we’ll discuss three measures of central tendency:

  • The mean

  • The median

  • And the mode

Now, this is not a statistics course, but here we briefly discuss these measures and some of their characteristics to make sure that we’re all on the same page when we discuss interpretation of our results.

The mean

When we talk about the typical, or “average”, value of some variable measured on a continuous scale, we are usually talking about the mean value of that variable, and to be even more specific we are usually talking about the arithmetic mean value. And this value has some favorable characteristics that make it a good description of central tendency.

  1. For starters it’s simple. Most people are familiar with the mean, and at the very least, have some intuitive sense of what it means (no pun intended).

  2. In addition, for any set of values, there can be only one mean value.

However, there are a couple of potentially problematic characteristics of the mean as well:

  1. It’s susceptible to extreme values in your data. In other words, a couple of people with very atypical values for the characteristic you are interested in, can drastically alter the value of the mean, and your estimate for the typical person in your population of interest along with it.

  2. Additionally, it’s very possible to calculate a mean value that is not actually observed anywhere in your data. For instance, the mean weight for our class could be 133 pounds even if nobody in the class actually weighs 133 pounds.

Note: The sample mean is often referred to as \(\bar{x}\), which pronounced “x bar.”

The median

The median is probably the second most commonly used measure of central tendency and like the mean it’s computationally simple and relatively straightforward to understand. There can be one, and only one, median. And, its value may also be unobserved in the data.

However, unlike the mean, it’s relatively resistant to extreme values. In fact, when the median is used as the measure of central tendency, it’s often because the person conducting the analysis suspects that extreme values in the data are likely to distort the mean.

The mode

And finally, we have the mode, or the value that is most often observed in the data. It doesn’t get much simpler than that. But, unlike the mean and the median, there can be more than one mode for a given set of values. In fact, there can even be no mode if all the values are observed the exact same number of times.

However, if there is a mode, by definition it’s observed in the data.

Now that we are all on the same page with respect to the fundamentals of central tendency, let’s take a look at how to calculate these measures using R.

Calculate the mean

Calculating the mean is really straightforward. We can just use base R’s built-in mean() function.

# Load the Tidyverse package
library(tidyverse)
# Simulate some data
height_and_weight_20 <- tribble(
  ~id,   ~sex,     ~ht_in, ~wt_lbs,
  "001", "Male",   71,     190,
  "002", "Male",   69,     177,
  "003", "Female", 64,     130,
  "004", "Female", 65,     153,
  "005", NA,       73,     173,
  "006", "Male",   69,     182,
  "007", "Female", 68,     186,
  "008", NA,       73,     185,
  "009", "Female", 71,     157,
  "010", "Male",   66,     155,
  "011", "Male",   71,     213,
  "012", "Female", 69,     151,
  "013", "Female", 66,     147,
  "014", "Female", 68,     196,
  "015", "Male",   75,     212,
  "016", "Female", 69,     19000,
  "017", "Female", 66,     194,
  "018", "Female", 65,     176,
  "019", "Female", 65,     176,
  "020", "Female", 65,     102
)
mean(height_and_weight_20$ht_in)
[1] 68.4

Here’s what we did above:

  • We started by simulating some data. Heights and weights for 20 hypothetical students.

  • Next, we used base R’s mean() function to calculate the mean of the column “ht_in” from the data frame “height_and_weight_20”.

    • Note: if you just type mean(ht_in) you will get an error. That’s because R will look for an object called “ht_in” in the global environment.

    • However, we didn’t create an object called “ht_in”. We created an object (in this case a data frame) called “height_and_weight_20”. That object has a column in it called “ht_in”.

    • So, we must specifically tell R to look for the “ht_in” column in the data frame “height_and_weight_20”. Using base R, we can do that in one of two ways: height_and_weight_20$ht_in or height_and_weight_20[["ht_in"]].

Calculate the median

Similar to above, we can use base R’s median() function to calculate the median.

median(height_and_weight_20$ht_in)
[1] 68.5

Here’s what we did above:

  • We used base R’s median() function to calculate the modian of the column “ht_in” from the data frame “height_and_weight_20”.

Calculate the mode

Base R does not have a built-in mode() function. Well, it actually does have a mode() function, but for some reason that function does not return the mode value(s) of a set of numbers. Instead, the mode() function gets or sets the type or storage mode of an object. For example:

mode(height_and_weight_20$ht_in)
[1] "numeric"

This is clearly not what we are looking for. So, how do we find the mode value(s)? Well, we are going to have to build our own mode function. I’ll start at the final result – our mode function. Then, I’ll walk you through how I built it one step at a time. Keep in mind, as is almost always the case with R, my way of writing this function is only one of multiple possible ways.

mode_val <- function(x) {
  
  # Count the number of occurrences for each value of x
  value_counts <- table(x)
  
  # Get the maximum number of times any value is observed
  max_count <- max(value_counts)
  
  # Create and index vector that identifies the positions that correspond to
  # count values that are the same as the maximum count value: TRUE if so
  # and false otherwise
  index <- value_counts == max_count
  
  # Use the index vector to get all values that are observed the same number 
  # of times as the maximum number of times that any value is observed
  unique_values <- names(value_counts)
  result <- unique_values[index]
  
  # If result is the same length as value counts that means that every value
  # occured the same number of times. If every value occurred the same number
  # of times, then there is no mode
  no_mode <- length(value_counts) == length(result)
  
  # If there is no mode then change the value of result to NA
  if (no_mode) {
    result <- NA
  }
  
  # Return result
  result
}
mode_val(height_and_weight_20$ht_in)
[1] "65" "69"

Here’s what we did above:

  • We created our own function, mode_val(), that takes a vector (or data frame column) as an argument to its “x” parameter, and returns the mode value(s) of that vector.

  • We can also see that the function works as expected when there is more than one mode value. In this case, “65” and “69” each occur 4 times in the column “ht_in”.

Now that we created our mode function, let’s test to make sure it also works when there is only one mode value and when there is no mode value.

# Test data for one mode value
one_mode <- c(1, 1, 2, 3, 4)
# Should return 1
mode_val(one_mode)
[1] "1"
# Test data for no mode value
no_mode <- c(1, 2, 3, 4, 5)
# Should return NA
mode_val(no_mode)
[1] NA

Here’s what we did above:

  • We tested our function, mode_val(), on a vector with one mode value and a vector with no mode value. We got the results we expected.

  • Now you may want to save this function somewhere so that you can use it again in the future if you need to.

Dissecting our mode function

You may have already figured out how our mode_val() function works by just looking through the comments I put inside the function. If so, great! If not, I walk through how it works step-by-step in this section.

First, we used the table() function to return a contingency table. A numeric vector that includes each unique value of “ht_in” along with the number of times each value occurs.

# Count the number of occurrences for each value of x
value_counts <- table(height_and_weight_20$ht_in)
value_counts

64 65 66 68 69 71 73 75 
 1  4  3  2  4  3  2  1 

Just looking at the contingency table above, you can see that the largest number of occurrences is 4. In other words, 4 students had a height of 65" and 4 students had a height of 69". If this were our real data, we would probably just stop here. We have our answer. However, it would be tedious and error-prone to manually check for the mode value this way if our real data included many more possible values.

Second, we figured out what the maximum number of times any value occurred.

# Get the maximum number of times any value is observed
max_count <- max(value_counts)
max_count
[1] 4

Third, we created an index vector that is the same length as “value_counts” (i.e. 8). The index vector has a value of TRUE in each position were “value_counts” is equal to “max_count” (i.e. 4).

# Create and index vector that identifies the positions that correspond to
# count values that are the same as the maximum count value: TRUE if so
# and false otherwise
index <- value_counts == max_count
index

   64    65    66    68    69    71    73    75 
FALSE  TRUE FALSE FALSE  TRUE FALSE FALSE FALSE 

Fourth, we use that index vector to keep only the values of “ht_in” that were observed 4 times – where “index” is TRUE.

unique_values <- names(value_counts)
unique_values
[1] "64" "65" "66" "68" "69" "71" "73" "75"
result <- unique_values[index]
result
[1] "65" "69"

We could stop here if we didn’t have to worry about vectors that don’t have a mode value. However, if we want our function to be as complete as possible, we should make sure it intelligently handles vectors that don’t have a mode value.

We will do that below using a conditional statement. If you need a refresher on conditional statements, you may want to work through Intermediate R, Chapter 1: Conditionals and Control Flow again.

So, the fifth step is to test the equality of the lengths of “value_counts” and “result”. At this point in our function “value_counts” is a vector that includes each unique value of “ht_in” along with the number of times each value occurs. Result is a vector that includes only the values of “ht_in” that were observed the same number of times as the maximum number of times any value was observed – in this case, 4. So, the lengths of “value_counts” and “result” are:

length(value_counts)
[1] 8
length(result)
[1] 2

And therefore…

length(value_counts) == length(result)
[1] FALSE

And therefore…

# If result is the same length as value counts that means that every value
# occured the same number of times. If every value occurred the same number
# of times, then there is no mode
no_mode <- length(value_counts) == length(result)
no_mode
[1] FALSE

But, what would happen in a case where each value appeared exactly one time?

ht_in_same <- c(68, 69, 70,71,72)
value_counts_same <- table(ht_in_same)
max_count_same <- max(value_counts_same)
index_same <- value_counts_same == max_count_same
unique_values_same <- names(value_counts_same)
result_same <- unique_values_same[index_same]
value_counts_same
ht_in_same
68 69 70 71 72 
 1  1  1  1  1 
result_same
[1] "68" "69" "70" "71" "72"
length(value_counts_same) == length(result_same)
[1] TRUE

As you can see, whenever each value appears the same number of times (in this case one time each) the length of “value_counts” and “result” will be equal. In that case (i.e. “no_mode” is TRUE), we want to overwrite the value of result to be NA (missing). In other words, when all values appear the same name of times, there is no mode value – it is missing.

Here’s what we did above:

  • We broke down our mode_val() function into its component parts. We viewed what each part does and how it ultimately contributes to a working function.

  • This version of the mode_val() function was especially long an verbose. I did this intentionally to help make it easier to understand how it works. And in this case, that really isn’t a problem. However, you can try rewriting this function in a more succinct way if you want to challenge yourself.

Compare mean, median, and mode

Now that you know how to calculate the mean, median, and mode, let’s compare these three measures of central tendency. This is a good opportunity to demonstrate some of the different characteristics of each that we spoke about earlier.

height_and_weight_20 %>% 
  summarise(
    min_weight    = min(wt_lbs),
    mean_weight   = mean(wt_lbs),
    median_weight = median(wt_lbs),
    mode_weight   = mode_val(wt_lbs) %>% as.double(),
    max_weight    = max(wt_lbs)
  )

Here’s what we did above:

  • We used the mean() function, median() function, and our mode_val() function inside of dpylr’s summarise() function to find the mean, median, and mode values of the column “wt_lbs” in the “height_and_weight_20” data frame.

  • We also used the as.double() function to convert the value returned by mode_val() – “176” – from a character string to a numeric double. This isn’t strictly necessary, but I think it looks better.

  • Finally, we used base R’s min() and max() functions to view the lowest and highest weights in our sample.

Data checking

Do you see any red flags 🚩as you scan the results? Do you really think a mean weight of 1,113 pounds sounds reasonable? This should definitely be a red flag for you. Now move your gaze three columns to the right and notice that the maximum value of weight is 19,000 lbs – an impossible value for a study in human populations. In this case the real weight was supposed to be 190 pounds, but the person entering the data accidently got a little trigger-happy with the zero key. This is an example of what I meant when I said “We can use descriptive analysis to uncover errors in our data” in the Introduction to Descriptive Analysis lesson. Often times, for various reasons, some observations for a given variable take on values that don’t make sense. Starting by calculating some basic descriptive statistics for each variable is one approach you can use to try to figure out if you have values in your data that don’t make sense.

In this case we can just go back and fix our data, but what if we didn’t know this value was an error? What if it were a value that was technically possible, but very unlikely? Well, we can’t just go changing values in our data. It’s unethical, and in some cases illegal. Below, we discuss the how the properties of the median and mode can come in handy in situations such as this.

Properties of mean, median, and mode

Despite the fact that this impossibly extreme value is in our data, the median and mode estimates are reasonable estimates of the average person’s weight in this sample. This is what I meant when I said that the median and mode were more “resistant to extreme values” than the mean.

You may also notice that no person in our sample had an actual weight of 1,112.75 (the mean) or even 176.5 (the median). This is what I meant above when I said that the mean and median values are “not necessarily observed in the data.”

In this case, the mode value (176) is also a more reasonable estimate of the average person’s weight than the mean. And unlike the mean and the median, participants 18 and 19 actually weigh 176 pounds. I’m not saying that the mode is always the best measure of central tendency to use. I am saying that you can often learn useful information from your data by calculating and comparing these relatively simple descriptive statistics on each of your numeric variables.

LS0tCnRpdGxlOiAiRGVzY3JpYmluZyBDZW50cmFsIFRlbmRlbmN5IgphdXRob3I6ICJCcmFkIENhbm5lbGwiCmRhdGU6ICJDcmVhdGVkOiAyMDE5LTA0LTIyIDxicj4gVXBkYXRlZDogYHIgU3lzLkRhdGUoKWAiCm91dHB1dDogCiAgaHRtbF9ub3RlYm9vazogCiAgICB0b2M6IHRydWUKICAgIHRvY19mbG9hdDogdHJ1ZQogICAgY3NzOiAiLi4vLi4vY3NzL2xtLW1hcmtkb3duLXN0eWxlcy5jc3MiCi0tLQoKYGBge3Igc2V0dXAsIGluY2x1ZGU9RkFMU0V9CmtuaXRyOjpvcHRzX2NodW5rJHNldChlY2hvID0gVFJVRSkKYGBgCgojIEludHJvZHVjdGlvbgoKSW4gcHJldmlvdXMgc2VjdGlvbnMgeW91J3ZlIHNlZW4gbWV0aG9kcyBmb3IgZGVzY3JpYmluZyBpbmRpdmlkdWFsIGNhdGVnb3JpY2FsIHZhcmlhYmxlcy4gTm93IHdl4oCZbGwgc3dpdGNoIG92ZXIgdG8gZGVzY3JpYmluZyBudW1lcmljYWwgdmFyaWFibGVzLgoKIVtdKGltZ19kZXNjcmlwdGl2ZV9hbmFseXNpcy9kZXNjcmlwdGl2ZV9hbmFseXNpc19mbG93Y2hhcnRfMDJfbnVtX29ubHkucG5nKQoKVGhlIG1vc3QgYmFzaWMsIGFuZCBwcm9iYWJseSBtb3N0IGNvbW1vbmx5IHVzZWQgd2F5IHRvIGRlc2NyaWJlIHRoZSB0eXBpY2FsIHBlcnNvbiBpbiBhIHBvcHVsYXRpb24gd2l0aCByZXNwZWN0IHRvIHNvbWUgY2hhcmFjdGVyaXN0aWMgdGhhdCBpcyByZWNvcmRlZCBhcyBhIG51bWVyaWNhbCB2YXJpYWJsZSwgbGlrZSBoZWlnaHQgb3Igd2VpZ2h0LCBpcyB3aXRoIGEgbWVhc3VyZSBvZiBjZW50cmFsIHRlbmRlbmN5LgpJbiB0aGlzIGxlY3R1cmUgd2XigJlsbCBkaXNjdXNzIHRocmVlIG1lYXN1cmVzIG9mIGNlbnRyYWwgdGVuZGVuY3k6CgoqIFRoZSBtZWFuCgoqIFRoZSBtZWRpYW4KCiogQW5kIHRoZSBtb2RlCgohW10oaW1nX2Rlc2NyaXB0aXZlX2FuYWx5c2lzL2NlbnRyYWxfdGVuZGVuY3lfMDEucG5nKQoKTm93LCB0aGlzIGlzIG5vdCBhIHN0YXRpc3RpY3MgY291cnNlLCBidXQgaGVyZSB3ZSBicmllZmx5IGRpc2N1c3MgdGhlc2UgbWVhc3VyZXMgYW5kIHNvbWUgb2YgdGhlaXIgY2hhcmFjdGVyaXN0aWNzIHRvIG1ha2Ugc3VyZSB0aGF0IHdl4oCZcmUgYWxsIG9uIHRoZSBzYW1lIHBhZ2Ugd2hlbiB3ZSBkaXNjdXNzIGludGVycHJldGF0aW9uIG9mIG91ciByZXN1bHRzLgoKIyMgVGhlIG1lYW4KCiFbXShpbWdfZGVzY3JpcHRpdmVfYW5hbHlzaXMvY2VudHJhbF90ZW5kZW5jeV8wMi5wbmcpCgpXaGVuIHdlIHRhbGsgYWJvdXQgdGhlIHR5cGljYWwsIG9yIOKAnGF2ZXJhZ2XigJ0sIHZhbHVlIG9mIHNvbWUgdmFyaWFibGUgbWVhc3VyZWQgb24gYSBjb250aW51b3VzIHNjYWxlLCB3ZSBhcmUgdXN1YWxseSB0YWxraW5nIGFib3V0IHRoZSBtZWFuIHZhbHVlIG9mIHRoYXQgdmFyaWFibGUsIGFuZCB0byBiZSBldmVuIG1vcmUgc3BlY2lmaWMgd2UgYXJlIHVzdWFsbHkgdGFsa2luZyBhYm91dCB0aGUgYXJpdGhtZXRpYyBtZWFuIHZhbHVlLiBBbmQgdGhpcyB2YWx1ZSBoYXMgc29tZSBmYXZvcmFibGUgY2hhcmFjdGVyaXN0aWNzIHRoYXQgbWFrZSBpdCBhIGdvb2QgZGVzY3JpcHRpb24gb2YgY2VudHJhbCB0ZW5kZW5jeS4KCjEuIEZvciBzdGFydGVycyBpdOKAmXMgc2ltcGxlLiBNb3N0IHBlb3BsZSBhcmUgZmFtaWxpYXIgd2l0aCB0aGUgbWVhbiwgYW5kIGF0IHRoZSB2ZXJ5IGxlYXN0LCBoYXZlIHNvbWUgaW50dWl0aXZlIHNlbnNlIG9mIHdoYXQgaXQgbWVhbnMgKG5vIHB1biBpbnRlbmRlZCkuIAoKMi4gSW4gYWRkaXRpb24sIGZvciBhbnkgc2V0IG9mIHZhbHVlcywgdGhlcmUgY2FuIGJlIG9ubHkgb25lIG1lYW4gdmFsdWUuIAoKSG93ZXZlciwgdGhlcmUgYXJlIGEgY291cGxlIG9mIHBvdGVudGlhbGx5IHByb2JsZW1hdGljIGNoYXJhY3RlcmlzdGljcyBvZiB0aGUgbWVhbiBhcyB3ZWxsOiAKCjEuIEl04oCZcyBzdXNjZXB0aWJsZSB0byBleHRyZW1lIHZhbHVlcyBpbiB5b3VyIGRhdGEuIEluIG90aGVyIHdvcmRzLCBhIGNvdXBsZSBvZiBwZW9wbGUgd2l0aCB2ZXJ5IGF0eXBpY2FsIHZhbHVlcyBmb3IgdGhlIGNoYXJhY3RlcmlzdGljIHlvdSBhcmUgaW50ZXJlc3RlZCBpbiwgY2FuIGRyYXN0aWNhbGx5IGFsdGVyIHRoZSB2YWx1ZSBvZiB0aGUgbWVhbiwgYW5kIHlvdXIgZXN0aW1hdGUgZm9yIHRoZSB0eXBpY2FsIHBlcnNvbiBpbiB5b3VyIHBvcHVsYXRpb24gb2YgaW50ZXJlc3QgYWxvbmcgd2l0aCBpdC4gPCEtLSBDb2RlIGFuIGV4YW1wbGUgLS0+CgoyLiBBZGRpdGlvbmFsbHksIGl04oCZcyB2ZXJ5IHBvc3NpYmxlIHRvIGNhbGN1bGF0ZSBhIG1lYW4gdmFsdWUgdGhhdCBpcyBub3QgYWN0dWFsbHkgb2JzZXJ2ZWQgYW55d2hlcmUgaW4geW91ciBkYXRhLiBGb3IgaW5zdGFuY2UsIHRoZSBtZWFuIHdlaWdodCBmb3Igb3VyIGNsYXNzIGNvdWxkIGJlIDEzMyBwb3VuZHMgZXZlbiBpZiBub2JvZHkgaW4gdGhlIGNsYXNzIGFjdHVhbGx5IHdlaWdocyAxMzMgcG91bmRzLiA8IS0tIENvZGUgYW4gZXhhbXBsZSAtLT4KCjxkaXYgY2xhc3M9Im1vcmUtaW5mbyI+Ck5vdGU6IFRoZSBzYW1wbGUgbWVhbiBpcyBvZnRlbiByZWZlcnJlZCB0byBhcyAkXGJhcnt4fSQsIHdoaWNoIHByb25vdW5jZWQgInggYmFyLiIKPC9kaXY+CgojIyBUaGUgbWVkaWFuCgohW10oaW1nX2Rlc2NyaXB0aXZlX2FuYWx5c2lzL2NlbnRyYWxfdGVuZGVuY3lfMDMucG5nKQoKVGhlIG1lZGlhbiBpcyBwcm9iYWJseSB0aGUgc2Vjb25kIG1vc3QgY29tbW9ubHkgdXNlZCBtZWFzdXJlIG9mIGNlbnRyYWwgdGVuZGVuY3kgYW5kIGxpa2UgdGhlIG1lYW4gaXTigJlzIGNvbXB1dGF0aW9uYWxseSBzaW1wbGUgYW5kIHJlbGF0aXZlbHkgc3RyYWlnaHRmb3J3YXJkIHRvIHVuZGVyc3RhbmQuIFRoZXJlIGNhbiBiZSBvbmUsIGFuZCBvbmx5IG9uZSwgbWVkaWFuLiBBbmQsIGl0cyB2YWx1ZSBtYXkgYWxzbyBiZSB1bm9ic2VydmVkIGluIHRoZSBkYXRhLgoKSG93ZXZlciwgdW5saWtlIHRoZSBtZWFuLCBpdOKAmXMgcmVsYXRpdmVseSByZXNpc3RhbnQgdG8gZXh0cmVtZSB2YWx1ZXM8IS0tIENvZGUgYW4gZXhhbXBsZSAtLT4uIEluIGZhY3QsIHdoZW4gdGhlIG1lZGlhbiBpcyB1c2VkIGFzIHRoZSBtZWFzdXJlIG9mIGNlbnRyYWwgdGVuZGVuY3ksIGl04oCZcyBvZnRlbiBiZWNhdXNlIHRoZSBwZXJzb24gY29uZHVjdGluZyB0aGUgYW5hbHlzaXMgc3VzcGVjdHMgdGhhdCBleHRyZW1lIHZhbHVlcyBpbiB0aGUgZGF0YSBhcmUgbGlrZWx5IHRvIGRpc3RvcnQgdGhlIG1lYW4uCgojIyBUaGUgbW9kZQoKIVtdKGltZ19kZXNjcmlwdGl2ZV9hbmFseXNpcy9jZW50cmFsX3RlbmRlbmN5XzA0LnBuZykKCkFuZCBmaW5hbGx5LCB3ZSBoYXZlIHRoZSBtb2RlLCBvciB0aGUgdmFsdWUgdGhhdCBpcyBtb3N0IG9mdGVuIG9ic2VydmVkIGluIHRoZSBkYXRhLiBJdCBkb2VzbuKAmXQgZ2V0IG11Y2ggc2ltcGxlciB0aGFuIHRoYXQuIEJ1dCwgdW5saWtlIHRoZSBtZWFuIGFuZCB0aGUgbWVkaWFuLCB0aGVyZSBjYW4gYmUgbW9yZSB0aGFuIG9uZSBtb2RlIGZvciBhIGdpdmVuIHNldCBvZiB2YWx1ZXMuIEluIGZhY3QsIHRoZXJlIGNhbiBldmVuIGJlIG5vIG1vZGUgaWYgYWxsIHRoZSB2YWx1ZXMgYXJlIG9ic2VydmVkIHRoZSBleGFjdCBzYW1lIG51bWJlciBvZiB0aW1lcy4KCkhvd2V2ZXIsIGlmIHRoZXJlIGlzIGEgbW9kZSwgYnkgZGVmaW5pdGlvbiBpdOKAmXMgb2JzZXJ2ZWQgaW4gdGhlIGRhdGEuCgpOb3cgdGhhdCB3ZSBhcmUgYWxsIG9uIHRoZSBzYW1lIHBhZ2Ugd2l0aCByZXNwZWN0IHRvIHRoZSBmdW5kYW1lbnRhbHMgb2YgY2VudHJhbCB0ZW5kZW5jeSwgbGV04oCZcyB0YWtlIGEgbG9vayBhdCBob3cgdG8gY2FsY3VsYXRlIHRoZXNlIG1lYXN1cmVzIHVzaW5nIFIuCgojIENhbGN1bGF0ZSB0aGUgbWVhbgoKQ2FsY3VsYXRpbmcgdGhlIG1lYW4gaXMgcmVhbGx5IHN0cmFpZ2h0Zm9yd2FyZC4gV2UgY2FuIGp1c3QgdXNlIGJhc2UgUidzIGJ1aWx0LWluIGBtZWFuKClgIGZ1bmN0aW9uLgoKYGBge3IgbWVzc2FnZT1GQUxTRX0KIyBMb2FkIHRoZSBUaWR5dmVyc2UgcGFja2FnZQpsaWJyYXJ5KHRpZHl2ZXJzZSkKYGBgCgpgYGB7cn0KIyBTaW11bGF0ZSBzb21lIGRhdGEKaGVpZ2h0X2FuZF93ZWlnaHRfMjAgPC0gdHJpYmJsZSgKICB+aWQsICAgfnNleCwgICAgIH5odF9pbiwgfnd0X2xicywKICAiMDAxIiwgIk1hbGUiLCAgIDcxLCAgICAgMTkwLAogICIwMDIiLCAiTWFsZSIsICAgNjksICAgICAxNzcsCiAgIjAwMyIsICJGZW1hbGUiLCA2NCwgICAgIDEzMCwKICAiMDA0IiwgIkZlbWFsZSIsIDY1LCAgICAgMTUzLAogICIwMDUiLCBOQSwgICAgICAgNzMsICAgICAxNzMsCiAgIjAwNiIsICJNYWxlIiwgICA2OSwgICAgIDE4MiwKICAiMDA3IiwgIkZlbWFsZSIsIDY4LCAgICAgMTg2LAogICIwMDgiLCBOQSwgICAgICAgNzMsICAgICAxODUsCiAgIjAwOSIsICJGZW1hbGUiLCA3MSwgICAgIDE1NywKICAiMDEwIiwgIk1hbGUiLCAgIDY2LCAgICAgMTU1LAogICIwMTEiLCAiTWFsZSIsICAgNzEsICAgICAyMTMsCiAgIjAxMiIsICJGZW1hbGUiLCA2OSwgICAgIDE1MSwKICAiMDEzIiwgIkZlbWFsZSIsIDY2LCAgICAgMTQ3LAogICIwMTQiLCAiRmVtYWxlIiwgNjgsICAgICAxOTYsCiAgIjAxNSIsICJNYWxlIiwgICA3NSwgICAgIDIxMiwKICAiMDE2IiwgIkZlbWFsZSIsIDY5LCAgICAgMTkwMDAsCiAgIjAxNyIsICJGZW1hbGUiLCA2NiwgICAgIDE5NCwKICAiMDE4IiwgIkZlbWFsZSIsIDY1LCAgICAgMTc2LAogICIwMTkiLCAiRmVtYWxlIiwgNjUsICAgICAxNzYsCiAgIjAyMCIsICJGZW1hbGUiLCA2NSwgICAgIDEwMgopCmBgYAoKYGBge3J9Cm1lYW4oaGVpZ2h0X2FuZF93ZWlnaHRfMjAkaHRfaW4pCmBgYAoKKipIZXJlJ3Mgd2hhdCB3ZSBkaWQgYWJvdmU6KioKCiogV2Ugc3RhcnRlZCBieSBzaW11bGF0aW5nIHNvbWUgZGF0YS4gSGVpZ2h0cyBhbmQgd2VpZ2h0cyBmb3IgMjAgaHlwb3RoZXRpY2FsIHN0dWRlbnRzLgoKKiBOZXh0LCB3ZSB1c2VkIGJhc2UgUidzIGBtZWFuKClgIGZ1bmN0aW9uIHRvIGNhbGN1bGF0ZSB0aGUgbWVhbiBvZiB0aGUgY29sdW1uICJodF9pbiIgZnJvbSB0aGUgZGF0YSBmcmFtZSAiaGVpZ2h0X2FuZF93ZWlnaHRfMjAiLgoKICAtIE5vdGU6IGlmIHlvdSBqdXN0IHR5cGUgYG1lYW4oaHRfaW4pYCB5b3Ugd2lsbCBnZXQgYW4gZXJyb3IuIFRoYXQncyBiZWNhdXNlIFIgd2lsbCBsb29rIGZvciBhbiBvYmplY3QgY2FsbGVkICJodF9pbiIgaW4gdGhlIGdsb2JhbCBlbnZpcm9ubWVudC4gCiAgCiAgLSBIb3dldmVyLCB3ZSBkaWRuJ3QgY3JlYXRlIGFuIG9iamVjdCBjYWxsZWQgImh0X2luIi4gV2UgY3JlYXRlZCBhbiBvYmplY3QgKGluIHRoaXMgY2FzZSBhIGRhdGEgZnJhbWUpIGNhbGxlZCAiaGVpZ2h0X2FuZF93ZWlnaHRfMjAiLiBUaGF0IG9iamVjdCBoYXMgYSBjb2x1bW4gaW4gaXQgY2FsbGVkICJodF9pbiIuCgogIC0gU28sIHdlIG11c3Qgc3BlY2lmaWNhbGx5IHRlbGwgUiB0byBsb29rIGZvciB0aGUgImh0X2luIiBjb2x1bW4gaW4gdGhlIGRhdGEgZnJhbWUgImhlaWdodF9hbmRfd2VpZ2h0XzIwIi4gVXNpbmcgYmFzZSBSLCB3ZSBjYW4gZG8gdGhhdCBpbiBvbmUgb2YgdHdvIHdheXM6IGBoZWlnaHRfYW5kX3dlaWdodF8yMCRodF9pbmAgb3IgYGhlaWdodF9hbmRfd2VpZ2h0XzIwW1siaHRfaW4iXV1gLgogIAojIENhbGN1bGF0ZSB0aGUgbWVkaWFuCgpTaW1pbGFyIHRvIGFib3ZlLCB3ZSBjYW4gdXNlIGJhc2UgUidzIGBtZWRpYW4oKWAgZnVuY3Rpb24gdG8gY2FsY3VsYXRlIHRoZSBtZWRpYW4uCgpgYGB7cn0KbWVkaWFuKGhlaWdodF9hbmRfd2VpZ2h0XzIwJGh0X2luKQpgYGAKCioqSGVyZSdzIHdoYXQgd2UgZGlkIGFib3ZlOioqCgoqIFdlIHVzZWQgYmFzZSBSJ3MgYG1lZGlhbigpYCBmdW5jdGlvbiB0byBjYWxjdWxhdGUgdGhlIG1vZGlhbiBvZiB0aGUgY29sdW1uICJodF9pbiIgZnJvbSB0aGUgZGF0YSBmcmFtZSAiaGVpZ2h0X2FuZF93ZWlnaHRfMjAiLgoKIyBDYWxjdWxhdGUgdGhlIG1vZGUKCkJhc2UgUiBkb2VzIG5vdCBoYXZlIGEgYnVpbHQtaW4gYG1vZGUoKWAgZnVuY3Rpb24uIFdlbGwsIGl0IGFjdHVhbGx5IGRvZXMgaGF2ZSBhIGBtb2RlKClgIGZ1bmN0aW9uLCBidXQgZm9yIHNvbWUgcmVhc29uIHRoYXQgZnVuY3Rpb24gZG9lcyBub3QgcmV0dXJuIHRoZSBtb2RlIHZhbHVlKHMpIG9mIGEgc2V0IG9mIG51bWJlcnMuIEluc3RlYWQsIHRoZSBgbW9kZSgpYCBmdW5jdGlvbiBnZXRzIG9yIHNldHMgdGhlIHR5cGUgb3Igc3RvcmFnZSBtb2RlIG9mIGFuIG9iamVjdC4gRm9yIGV4YW1wbGU6CgpgYGB7cn0KbW9kZShoZWlnaHRfYW5kX3dlaWdodF8yMCRodF9pbikKYGBgCgpUaGlzIGlzIGNsZWFybHkgbm90IHdoYXQgd2UgYXJlIGxvb2tpbmcgZm9yLiBTbywgaG93IGRvIHdlIGZpbmQgdGhlIG1vZGUgdmFsdWUocyk/IFdlbGwsIHdlIGFyZSBnb2luZyB0byBoYXZlIHRvIGJ1aWxkIG91ciBvd24gbW9kZSBmdW5jdGlvbi4gSSdsbCBzdGFydCBhdCB0aGUgZmluYWwgcmVzdWx0IC0tIG91ciBtb2RlIGZ1bmN0aW9uLiBUaGVuLCBJJ2xsIHdhbGsgeW91IHRocm91Z2ggaG93IEkgYnVpbHQgaXQgb25lIHN0ZXAgYXQgYSB0aW1lLiBLZWVwIGluIG1pbmQsIGFzIGlzIGFsbW9zdCBhbHdheXMgdGhlIGNhc2Ugd2l0aCBSLCBteSB3YXkgb2Ygd3JpdGluZyB0aGlzIGZ1bmN0aW9uIGlzIG9ubHkgb25lIG9mIG11bHRpcGxlIHBvc3NpYmxlIHdheXMuCgpgYGB7cn0KbW9kZV92YWwgPC0gZnVuY3Rpb24oeCkgewogIAogICMgQ291bnQgdGhlIG51bWJlciBvZiBvY2N1cnJlbmNlcyBmb3IgZWFjaCB2YWx1ZSBvZiB4CiAgdmFsdWVfY291bnRzIDwtIHRhYmxlKHgpCiAgCiAgIyBHZXQgdGhlIG1heGltdW0gbnVtYmVyIG9mIHRpbWVzIGFueSB2YWx1ZSBpcyBvYnNlcnZlZAogIG1heF9jb3VudCA8LSBtYXgodmFsdWVfY291bnRzKQogIAogICMgQ3JlYXRlIGFuZCBpbmRleCB2ZWN0b3IgdGhhdCBpZGVudGlmaWVzIHRoZSBwb3NpdGlvbnMgdGhhdCBjb3JyZXNwb25kIHRvCiAgIyBjb3VudCB2YWx1ZXMgdGhhdCBhcmUgdGhlIHNhbWUgYXMgdGhlIG1heGltdW0gY291bnQgdmFsdWU6IFRSVUUgaWYgc28KICAjIGFuZCBmYWxzZSBvdGhlcndpc2UKICBpbmRleCA8LSB2YWx1ZV9jb3VudHMgPT0gbWF4X2NvdW50CiAgCiAgIyBVc2UgdGhlIGluZGV4IHZlY3RvciB0byBnZXQgYWxsIHZhbHVlcyB0aGF0IGFyZSBvYnNlcnZlZCB0aGUgc2FtZSBudW1iZXIgCiAgIyBvZiB0aW1lcyBhcyB0aGUgbWF4aW11bSBudW1iZXIgb2YgdGltZXMgdGhhdCBhbnkgdmFsdWUgaXMgb2JzZXJ2ZWQKICB1bmlxdWVfdmFsdWVzIDwtIG5hbWVzKHZhbHVlX2NvdW50cykKICByZXN1bHQgPC0gdW5pcXVlX3ZhbHVlc1tpbmRleF0KICAKICAjIElmIHJlc3VsdCBpcyB0aGUgc2FtZSBsZW5ndGggYXMgdmFsdWUgY291bnRzIHRoYXQgbWVhbnMgdGhhdCBldmVyeSB2YWx1ZQogICMgb2NjdXJlZCB0aGUgc2FtZSBudW1iZXIgb2YgdGltZXMuIElmIGV2ZXJ5IHZhbHVlIG9jY3VycmVkIHRoZSBzYW1lIG51bWJlcgogICMgb2YgdGltZXMsIHRoZW4gdGhlcmUgaXMgbm8gbW9kZQogIG5vX21vZGUgPC0gbGVuZ3RoKHZhbHVlX2NvdW50cykgPT0gbGVuZ3RoKHJlc3VsdCkKICAKICAjIElmIHRoZXJlIGlzIG5vIG1vZGUgdGhlbiBjaGFuZ2UgdGhlIHZhbHVlIG9mIHJlc3VsdCB0byBOQQogIGlmIChub19tb2RlKSB7CiAgICByZXN1bHQgPC0gTkEKICB9CiAgCiAgIyBSZXR1cm4gcmVzdWx0CiAgcmVzdWx0Cn0KYGBgCgpgYGB7cn0KbW9kZV92YWwoaGVpZ2h0X2FuZF93ZWlnaHRfMjAkaHRfaW4pCmBgYAoKKipIZXJlJ3Mgd2hhdCB3ZSBkaWQgYWJvdmU6KioKCiogV2UgY3JlYXRlZCBvdXIgb3duIGZ1bmN0aW9uLCBgbW9kZV92YWwoKWAsIHRoYXQgdGFrZXMgYSB2ZWN0b3IgKG9yIGRhdGEgZnJhbWUgY29sdW1uKSBhcyBhbiBhcmd1bWVudCB0byBpdHMgIngiIHBhcmFtZXRlciwgYW5kIHJldHVybnMgdGhlIG1vZGUgdmFsdWUocykgb2YgdGhhdCB2ZWN0b3IuCgoqIFdlIGNhbiBhbHNvIHNlZSB0aGF0IHRoZSBmdW5jdGlvbiB3b3JrcyBhcyBleHBlY3RlZCB3aGVuIHRoZXJlIGlzIG1vcmUgdGhhbiBvbmUgbW9kZSB2YWx1ZS4gSW4gdGhpcyBjYXNlLCAiNjUiIGFuZCAiNjkiIGVhY2ggb2NjdXIgNCB0aW1lcyBpbiB0aGUgY29sdW1uICJodF9pbiIuIAoKTm93IHRoYXQgd2UgY3JlYXRlZCBvdXIgbW9kZSBmdW5jdGlvbiwgbGV0J3MgdGVzdCB0byBtYWtlIHN1cmUgaXQgYWxzbyB3b3JrcyB3aGVuIHRoZXJlIGlzIG9ubHkgb25lIG1vZGUgdmFsdWUgYW5kIHdoZW4gdGhlcmUgaXMgbm8gbW9kZSB2YWx1ZS4KCmBgYHtyfQojIFRlc3QgZGF0YSBmb3Igb25lIG1vZGUgdmFsdWUKb25lX21vZGUgPC0gYygxLCAxLCAyLCAzLCA0KQpgYGAKCmBgYHtyfQojIFNob3VsZCByZXR1cm4gMQptb2RlX3ZhbChvbmVfbW9kZSkKYGBgCgpgYGB7cn0KIyBUZXN0IGRhdGEgZm9yIG5vIG1vZGUgdmFsdWUKbm9fbW9kZSA8LSBjKDEsIDIsIDMsIDQsIDUpCmBgYAoKYGBge3J9CiMgU2hvdWxkIHJldHVybiBOQQptb2RlX3ZhbChub19tb2RlKQpgYGAKCioqSGVyZSdzIHdoYXQgd2UgZGlkIGFib3ZlOioqCgoqIFdlIHRlc3RlZCBvdXIgZnVuY3Rpb24sIGBtb2RlX3ZhbCgpYCwgb24gYSB2ZWN0b3Igd2l0aCBvbmUgbW9kZSB2YWx1ZSBhbmQgYSB2ZWN0b3Igd2l0aCBubyBtb2RlIHZhbHVlLiBXZSBnb3QgdGhlIHJlc3VsdHMgd2UgZXhwZWN0ZWQuCgoqIE5vdyB5b3UgbWF5IHdhbnQgdG8gc2F2ZSB0aGlzIGZ1bmN0aW9uIHNvbWV3aGVyZSBzbyB0aGF0IHlvdSBjYW4gdXNlIGl0IGFnYWluIGluIHRoZSBmdXR1cmUgaWYgeW91IG5lZWQgdG8uCgojIyBEaXNzZWN0aW5nIG91ciBtb2RlIGZ1bmN0aW9uCgpZb3UgbWF5IGhhdmUgYWxyZWFkeSBmaWd1cmVkIG91dCBob3cgb3VyIGBtb2RlX3ZhbCgpYCBmdW5jdGlvbiB3b3JrcyBieSBqdXN0IGxvb2tpbmcgdGhyb3VnaCB0aGUgY29tbWVudHMgSSBwdXQgaW5zaWRlIHRoZSBmdW5jdGlvbi4gSWYgc28sIGdyZWF0ISBJZiBub3QsIEkgd2FsayB0aHJvdWdoIGhvdyBpdCB3b3JrcyBzdGVwLWJ5LXN0ZXAgaW4gdGhpcyBzZWN0aW9uLgoKRmlyc3QsIHdlIHVzZWQgdGhlIGB0YWJsZSgpYCBmdW5jdGlvbiB0byByZXR1cm4gYSBjb250aW5nZW5jeSB0YWJsZS4gQSBudW1lcmljIHZlY3RvciB0aGF0IGluY2x1ZGVzIGVhY2ggdW5pcXVlIHZhbHVlIG9mICJodF9pbiIgYWxvbmcgd2l0aCB0aGUgbnVtYmVyIG9mIHRpbWVzIGVhY2ggdmFsdWUgb2NjdXJzLgoKYGBge3J9CiMgQ291bnQgdGhlIG51bWJlciBvZiBvY2N1cnJlbmNlcyBmb3IgZWFjaCB2YWx1ZSBvZiB4CnZhbHVlX2NvdW50cyA8LSB0YWJsZShoZWlnaHRfYW5kX3dlaWdodF8yMCRodF9pbikKdmFsdWVfY291bnRzCmBgYAoKSnVzdCBsb29raW5nIGF0IHRoZSBjb250aW5nZW5jeSB0YWJsZSBhYm92ZSwgeW91IGNhbiBzZWUgdGhhdCB0aGUgbGFyZ2VzdCBudW1iZXIgb2Ygb2NjdXJyZW5jZXMgaXMgNC4gSW4gb3RoZXIgd29yZHMsIDQgc3R1ZGVudHMgaGFkIGEgaGVpZ2h0IG9mIDY1IiBhbmQgNCBzdHVkZW50cyBoYWQgYSBoZWlnaHQgb2YgNjkiLiBJZiB0aGlzIHdlcmUgb3VyIHJlYWwgZGF0YSwgd2Ugd291bGQgcHJvYmFibHkganVzdCBzdG9wIGhlcmUuIFdlIGhhdmUgb3VyIGFuc3dlci4gSG93ZXZlciwgaXQgd291bGQgYmUgdGVkaW91cyBhbmQgZXJyb3ItcHJvbmUgdG8gbWFudWFsbHkgY2hlY2sgZm9yIHRoZSBtb2RlIHZhbHVlIHRoaXMgd2F5IGlmIG91ciByZWFsIGRhdGEgaW5jbHVkZWQgbWFueSBtb3JlIHBvc3NpYmxlIHZhbHVlcy4KClNlY29uZCwgd2UgZmlndXJlZCBvdXQgd2hhdCB0aGUgbWF4aW11bSBudW1iZXIgb2YgdGltZXMgYW55IHZhbHVlIG9jY3VycmVkLiAKCmBgYHtyfQojIEdldCB0aGUgbWF4aW11bSBudW1iZXIgb2YgdGltZXMgYW55IHZhbHVlIGlzIG9ic2VydmVkCm1heF9jb3VudCA8LSBtYXgodmFsdWVfY291bnRzKQptYXhfY291bnQKYGBgCgpUaGlyZCwgd2UgY3JlYXRlZCBhbiBpbmRleCB2ZWN0b3IgdGhhdCBpcyB0aGUgc2FtZSBsZW5ndGggYXMgInZhbHVlX2NvdW50cyIgKGkuZS4gOCkuIFRoZSBpbmRleCB2ZWN0b3IgaGFzIGEgdmFsdWUgb2YgVFJVRSBpbiBlYWNoIHBvc2l0aW9uIHdlcmUgInZhbHVlX2NvdW50cyIgaXMgZXF1YWwgdG8gIm1heF9jb3VudCIgKGkuZS4gNCkuCgpgYGB7cn0KIyBDcmVhdGUgYW5kIGluZGV4IHZlY3RvciB0aGF0IGlkZW50aWZpZXMgdGhlIHBvc2l0aW9ucyB0aGF0IGNvcnJlc3BvbmQgdG8KIyBjb3VudCB2YWx1ZXMgdGhhdCBhcmUgdGhlIHNhbWUgYXMgdGhlIG1heGltdW0gY291bnQgdmFsdWU6IFRSVUUgaWYgc28KIyBhbmQgZmFsc2Ugb3RoZXJ3aXNlCmluZGV4IDwtIHZhbHVlX2NvdW50cyA9PSBtYXhfY291bnQKaW5kZXgKYGBgCgpGb3VydGgsIHdlIHVzZSB0aGF0IGluZGV4IHZlY3RvciB0byBrZWVwIG9ubHkgdGhlIHZhbHVlcyBvZiAiaHRfaW4iIHRoYXQgd2VyZSBvYnNlcnZlZCA0IHRpbWVzIC0tIHdoZXJlICJpbmRleCIgaXMgVFJVRS4KCmBgYHtyfQp1bmlxdWVfdmFsdWVzIDwtIG5hbWVzKHZhbHVlX2NvdW50cykKdW5pcXVlX3ZhbHVlcwpgYGAKCmBgYHtyfQpyZXN1bHQgPC0gdW5pcXVlX3ZhbHVlc1tpbmRleF0KcmVzdWx0CmBgYAoKV2UgY291bGQgc3RvcCBoZXJlIGlmIHdlIGRpZG4ndCBoYXZlIHRvIHdvcnJ5IGFib3V0IHZlY3RvcnMgdGhhdCBkb24ndCBoYXZlIGEgbW9kZSB2YWx1ZS4gSG93ZXZlciwgaWYgd2Ugd2FudCBvdXIgZnVuY3Rpb24gdG8gYmUgYXMgY29tcGxldGUgYXMgcG9zc2libGUsIHdlIHNob3VsZCBtYWtlIHN1cmUgaXQgaW50ZWxsaWdlbnRseSBoYW5kbGVzIHZlY3RvcnMgdGhhdCBkb24ndCBoYXZlIGEgbW9kZSB2YWx1ZS4KCldlIHdpbGwgZG8gdGhhdCBiZWxvdyB1c2luZyBhIGNvbmRpdGlvbmFsIHN0YXRlbWVudC4gSWYgeW91IG5lZWQgYSByZWZyZXNoZXIgb24gY29uZGl0aW9uYWwgc3RhdGVtZW50cywgeW91IG1heSB3YW50IHRvIHdvcmsgdGhyb3VnaCBbSW50ZXJtZWRpYXRlIFIsIENoYXB0ZXIgMTogQ29uZGl0aW9uYWxzIGFuZCBDb250cm9sIEZsb3ddKGh0dHBzOi8vd3d3LmRhdGFjYW1wLmNvbS9jb3Vyc2VzL2ludGVybWVkaWF0ZS1yKSBhZ2Fpbi4KClNvLCB0aGUgZmlmdGggc3RlcCBpcyB0byB0ZXN0IHRoZSBlcXVhbGl0eSBvZiB0aGUgbGVuZ3RocyBvZiAidmFsdWVfY291bnRzIiBhbmQgInJlc3VsdCIuIEF0IHRoaXMgcG9pbnQgaW4gb3VyIGZ1bmN0aW9uICJ2YWx1ZV9jb3VudHMiIGlzIGEgdmVjdG9yIHRoYXQgaW5jbHVkZXMgZWFjaCB1bmlxdWUgdmFsdWUgb2YgImh0X2luIiBhbG9uZyB3aXRoIHRoZSBudW1iZXIgb2YgdGltZXMgZWFjaCB2YWx1ZSBvY2N1cnMuIFJlc3VsdCBpcyBhIHZlY3RvciB0aGF0IGluY2x1ZGVzIG9ubHkgdGhlIHZhbHVlcyBvZiAiaHRfaW4iIHRoYXQgd2VyZSBvYnNlcnZlZCB0aGUgc2FtZSBudW1iZXIgb2YgdGltZXMgYXMgdGhlIG1heGltdW0gbnVtYmVyIG9mIHRpbWVzIGFueSB2YWx1ZSB3YXMgb2JzZXJ2ZWQgLS0gaW4gdGhpcyBjYXNlLCA0LiBTbywgdGhlIGxlbmd0aHMgb2YgInZhbHVlX2NvdW50cyIgYW5kICJyZXN1bHQiIGFyZToKCmBgYHtyfQpsZW5ndGgodmFsdWVfY291bnRzKQpgYGAKCmBgYHtyfQpsZW5ndGgocmVzdWx0KQpgYGAKCkFuZCB0aGVyZWZvcmUuLi4KCmBgYHtyfQpsZW5ndGgodmFsdWVfY291bnRzKSA9PSBsZW5ndGgocmVzdWx0KQpgYGAKCkFuZCB0aGVyZWZvcmUuLi4KCmBgYHtyfQojIElmIHJlc3VsdCBpcyB0aGUgc2FtZSBsZW5ndGggYXMgdmFsdWUgY291bnRzIHRoYXQgbWVhbnMgdGhhdCBldmVyeSB2YWx1ZQojIG9jY3VyZWQgdGhlIHNhbWUgbnVtYmVyIG9mIHRpbWVzLiBJZiBldmVyeSB2YWx1ZSBvY2N1cnJlZCB0aGUgc2FtZSBudW1iZXIKIyBvZiB0aW1lcywgdGhlbiB0aGVyZSBpcyBubyBtb2RlCm5vX21vZGUgPC0gbGVuZ3RoKHZhbHVlX2NvdW50cykgPT0gbGVuZ3RoKHJlc3VsdCkKbm9fbW9kZQpgYGAKCkJ1dCwgd2hhdCB3b3VsZCBoYXBwZW4gaW4gYSBjYXNlIHdoZXJlIGVhY2ggdmFsdWUgYXBwZWFyZWQgZXhhY3RseSBvbmUgdGltZT8KCmBgYHtyfQpodF9pbl9zYW1lIDwtIGMoNjgsIDY5LCA3MCw3MSw3MikKdmFsdWVfY291bnRzX3NhbWUgPC0gdGFibGUoaHRfaW5fc2FtZSkKbWF4X2NvdW50X3NhbWUgPC0gbWF4KHZhbHVlX2NvdW50c19zYW1lKQppbmRleF9zYW1lIDwtIHZhbHVlX2NvdW50c19zYW1lID09IG1heF9jb3VudF9zYW1lCnVuaXF1ZV92YWx1ZXNfc2FtZSA8LSBuYW1lcyh2YWx1ZV9jb3VudHNfc2FtZSkKcmVzdWx0X3NhbWUgPC0gdW5pcXVlX3ZhbHVlc19zYW1lW2luZGV4X3NhbWVdCmBgYAoKYGBge3J9CnZhbHVlX2NvdW50c19zYW1lCmBgYAoKYGBge3J9CnJlc3VsdF9zYW1lCmBgYAoKYGBge3J9Cmxlbmd0aCh2YWx1ZV9jb3VudHNfc2FtZSkgPT0gbGVuZ3RoKHJlc3VsdF9zYW1lKQpgYGAKCkFzIHlvdSBjYW4gc2VlLCB3aGVuZXZlciBlYWNoIHZhbHVlIGFwcGVhcnMgdGhlIHNhbWUgbnVtYmVyIG9mIHRpbWVzIChpbiB0aGlzIGNhc2Ugb25lIHRpbWUgZWFjaCkgdGhlIGxlbmd0aCBvZiAidmFsdWVfY291bnRzIiBhbmQgInJlc3VsdCIgd2lsbCBiZSBlcXVhbC4gSW4gdGhhdCBjYXNlIChpLmUuICJub19tb2RlIiBpcyBUUlVFKSwgd2Ugd2FudCB0byBvdmVyd3JpdGUgdGhlIHZhbHVlIG9mIHJlc3VsdCB0byBiZSBOQSAobWlzc2luZykuIEluIG90aGVyIHdvcmRzLCB3aGVuIGFsbCB2YWx1ZXMgYXBwZWFyIHRoZSBzYW1lIG5hbWUgb2YgdGltZXMsIHRoZXJlIGlzIG5vIG1vZGUgdmFsdWUgLS0gaXQgaXMgbWlzc2luZy4KCioqSGVyZSdzIHdoYXQgd2UgZGlkIGFib3ZlOioqCgoqIFdlIGJyb2tlIGRvd24gb3VyIGBtb2RlX3ZhbCgpYCBmdW5jdGlvbiBpbnRvIGl0cyBjb21wb25lbnQgcGFydHMuIFdlIHZpZXdlZCB3aGF0IGVhY2ggcGFydCBkb2VzIGFuZCBob3cgaXQgdWx0aW1hdGVseSBjb250cmlidXRlcyB0byBhIHdvcmtpbmcgZnVuY3Rpb24uIAoKKiBUaGlzIHZlcnNpb24gb2YgdGhlIGBtb2RlX3ZhbCgpYCBmdW5jdGlvbiB3YXMgZXNwZWNpYWxseSBsb25nIGFuIHZlcmJvc2UuIEkgZGlkIHRoaXMgaW50ZW50aW9uYWxseSB0byBoZWxwIG1ha2UgaXQgZWFzaWVyIHRvIHVuZGVyc3RhbmQgaG93IGl0IHdvcmtzLiBBbmQgaW4gdGhpcyBjYXNlLCB0aGF0IHJlYWxseSBpc24ndCBhIHByb2JsZW0uIEhvd2V2ZXIsIHlvdSBjYW4gdHJ5IHJld3JpdGluZyB0aGlzIGZ1bmN0aW9uIGluIGEgbW9yZSBzdWNjaW5jdCB3YXkgaWYgeW91IHdhbnQgdG8gY2hhbGxlbmdlIHlvdXJzZWxmLgoKIyBDb21wYXJlIG1lYW4sIG1lZGlhbiwgYW5kIG1vZGUKCjwhLS0gVGhpcyBpc24ndCBIT1cgdG8gZ2V0IHRoZSBtZWFuLiBUaGlzIGEgY29tcGFyaXNvbiBvZiB0aGUgbWVhbiBhbmQgdGhlIG1lZGlhbiAtIHRoZSBXSFkgLS0+CgpOb3cgdGhhdCB5b3Uga25vdyBob3cgdG8gY2FsY3VsYXRlIHRoZSBtZWFuLCBtZWRpYW4sIGFuZCBtb2RlLCBsZXQncyBjb21wYXJlIHRoZXNlIHRocmVlIG1lYXN1cmVzIG9mIGNlbnRyYWwgdGVuZGVuY3kuIFRoaXMgaXMgYSBnb29kIG9wcG9ydHVuaXR5IHRvIGRlbW9uc3RyYXRlIHNvbWUgb2YgdGhlIGRpZmZlcmVudCBjaGFyYWN0ZXJpc3RpY3Mgb2YgZWFjaCB0aGF0IHdlIHNwb2tlIGFib3V0IGVhcmxpZXIuCgpgYGB7cn0KaGVpZ2h0X2FuZF93ZWlnaHRfMjAgJT4lIAogIHN1bW1hcmlzZSgKICAgIG1pbl93ZWlnaHQgICAgPSBtaW4od3RfbGJzKSwKICAgIG1lYW5fd2VpZ2h0ICAgPSBtZWFuKHd0X2xicyksCiAgICBtZWRpYW5fd2VpZ2h0ID0gbWVkaWFuKHd0X2xicyksCiAgICBtb2RlX3dlaWdodCAgID0gbW9kZV92YWwod3RfbGJzKSAlPiUgYXMuZG91YmxlKCksCiAgICBtYXhfd2VpZ2h0ICAgID0gbWF4KHd0X2xicykKICApCmBgYAoKKipIZXJlJ3Mgd2hhdCB3ZSBkaWQgYWJvdmU6KioKCiogV2UgdXNlZCB0aGUgYG1lYW4oKWAgZnVuY3Rpb24sIGBtZWRpYW4oKWAgZnVuY3Rpb24sIGFuZCBvdXIgYG1vZGVfdmFsKClgIGZ1bmN0aW9uIGluc2lkZSBvZiBkcHlscidzIGBzdW1tYXJpc2UoKWAgZnVuY3Rpb24gdG8gZmluZCB0aGUgbWVhbiwgbWVkaWFuLCBhbmQgbW9kZSB2YWx1ZXMgb2YgdGhlIGNvbHVtbiAid3RfbGJzIiBpbiB0aGUgImhlaWdodF9hbmRfd2VpZ2h0XzIwIiBkYXRhIGZyYW1lLgoKKiBXZSBhbHNvIHVzZWQgdGhlIGBhcy5kb3VibGUoKWAgZnVuY3Rpb24gdG8gY29udmVydCB0aGUgdmFsdWUgcmV0dXJuZWQgYnkgYG1vZGVfdmFsKClgIC0tICIxNzYiIC0tIGZyb20gYSBjaGFyYWN0ZXIgc3RyaW5nIHRvIGEgbnVtZXJpYyBkb3VibGUuIFRoaXMgaXNuJ3Qgc3RyaWN0bHkgbmVjZXNzYXJ5LCBidXQgSSB0aGluayBpdCBsb29rcyBiZXR0ZXIuCgoqIEZpbmFsbHksIHdlIHVzZWQgYmFzZSBSJ3MgYG1pbigpYCBhbmQgYG1heCgpYCBmdW5jdGlvbnMgdG8gdmlldyB0aGUgbG93ZXN0IGFuZCBoaWdoZXN0IHdlaWdodHMgaW4gb3VyIHNhbXBsZS4gCgojIyBEYXRhIGNoZWNraW5nCgpEbyB5b3Ugc2VlIGFueSByZWQgZmxhZ3Mg8J+aqWFzIHlvdSBzY2FuIHRoZSByZXN1bHRzPyBEbyB5b3UgcmVhbGx5IHRoaW5rIGEgbWVhbiB3ZWlnaHQgb2YgMSwxMTMgcG91bmRzIHNvdW5kcyByZWFzb25hYmxlPyBUaGlzIHNob3VsZCBkZWZpbml0ZWx5IGJlIGEgcmVkIGZsYWcgZm9yIHlvdS4gTm93IG1vdmUgeW91ciBnYXplIHRocmVlIGNvbHVtbnMgdG8gdGhlIHJpZ2h0IGFuZCBub3RpY2UgdGhhdCB0aGUgbWF4aW11bSB2YWx1ZSBvZiB3ZWlnaHQgaXMgMTksMDAwIGxicyDigJMgYW4gaW1wb3NzaWJsZSB2YWx1ZSBmb3IgYSBzdHVkeSBpbiBodW1hbiBwb3B1bGF0aW9ucy4gSW4gdGhpcyBjYXNlIHRoZSByZWFsIHdlaWdodCB3YXMgc3VwcG9zZWQgdG8gYmUgMTkwIHBvdW5kcywgYnV0IHRoZSBwZXJzb24gZW50ZXJpbmcgdGhlIGRhdGEgYWNjaWRlbnRseSBnb3QgYSBsaXR0bGUgdHJpZ2dlci1oYXBweSB3aXRoIHRoZSB6ZXJvIGtleS4gVGhpcyBpcyBhbiBleGFtcGxlIG9mIHdoYXQgSSBtZWFudCB3aGVuIEkgc2FpZCAiV2UgY2FuIHVzZSBkZXNjcmlwdGl2ZSBhbmFseXNpcyB0byB1bmNvdmVyIGVycm9ycyBpbiBvdXIgZGF0YSIgaW4gdGhlIFtJbnRyb2R1Y3Rpb24gdG8gRGVzY3JpcHRpdmUgQW5hbHlzaXMgbGVzc29uXSgwMF9pbnRyb2R1Y3Rpb24ubmIuaHRtbCkuIE9mdGVuIHRpbWVzLCBmb3IgdmFyaW91cyByZWFzb25zLCBzb21lIG9ic2VydmF0aW9ucyBmb3IgYSBnaXZlbiB2YXJpYWJsZSB0YWtlIG9uIHZhbHVlcyB0aGF0IGRvbuKAmXQgbWFrZSBzZW5zZS4gU3RhcnRpbmcgYnkgY2FsY3VsYXRpbmcgc29tZSBiYXNpYyBkZXNjcmlwdGl2ZSBzdGF0aXN0aWNzIGZvciBlYWNoIHZhcmlhYmxlIGlzIG9uZSBhcHByb2FjaCB5b3UgY2FuIHVzZSB0byB0cnkgdG8gZmlndXJlIG91dCBpZiB5b3UgaGF2ZSB2YWx1ZXMgaW4geW91ciBkYXRhIHRoYXQgZG9u4oCZdCBtYWtlIHNlbnNlLgoKSW4gdGhpcyBjYXNlIHdlIGNhbiBqdXN0IGdvIGJhY2sgYW5kIGZpeCBvdXIgZGF0YSwgYnV0IHdoYXQgaWYgd2UgZGlkbuKAmXQga25vdyB0aGlzIHZhbHVlIHdhcyBhbiBlcnJvcj8gV2hhdCBpZiBpdCB3ZXJlIGEgdmFsdWUgdGhhdCB3YXMgdGVjaG5pY2FsbHkgcG9zc2libGUsIGJ1dCB2ZXJ5IHVubGlrZWx5PyBXZWxsLCB3ZSBjYW7igJl0IGp1c3QgZ28gY2hhbmdpbmcgdmFsdWVzIGluIG91ciBkYXRhLiBJdOKAmXMgdW5ldGhpY2FsLCBhbmQgaW4gc29tZSBjYXNlcyBpbGxlZ2FsLiBCZWxvdywgd2UgZGlzY3VzcyB0aGUgaG93IHRoZSBwcm9wZXJ0aWVzIG9mIHRoZSBtZWRpYW4gYW5kIG1vZGUgY2FuIGNvbWUgaW4gaGFuZHkgaW4gc2l0dWF0aW9ucyBzdWNoIGFzIHRoaXMuCgojIyBQcm9wZXJ0aWVzIG9mIG1lYW4sIG1lZGlhbiwgYW5kIG1vZGUgCgpEZXNwaXRlIHRoZSBmYWN0IHRoYXQgdGhpcyBpbXBvc3NpYmx5IGV4dHJlbWUgdmFsdWUgaXMgaW4gb3VyIGRhdGEsIHRoZSBtZWRpYW4gYW5kIG1vZGUgZXN0aW1hdGVzIGFyZSByZWFzb25hYmxlIGVzdGltYXRlcyBvZiB0aGUgYXZlcmFnZSBwZXJzb27igJlzIHdlaWdodCBpbiB0aGlzIHNhbXBsZS4gVGhpcyBpcyB3aGF0IEkgbWVhbnQgd2hlbiBJIHNhaWQgdGhhdCB0aGUgbWVkaWFuIGFuZCBtb2RlIHdlcmUgbW9yZSDigJxyZXNpc3RhbnQgdG8gZXh0cmVtZSB2YWx1ZXPigJ0gdGhhbiB0aGUgbWVhbi4gCgpZb3UgbWF5IGFsc28gbm90aWNlIHRoYXQgbm8gcGVyc29uIGluIG91ciBzYW1wbGUgaGFkIGFuIGFjdHVhbCB3ZWlnaHQgb2YgMSwxMTIuNzUgKHRoZSBtZWFuKSBvciBldmVuIDE3Ni41ICh0aGUgbWVkaWFuKS4gVGhpcyBpcyB3aGF0IEkgbWVhbnQgYWJvdmUgd2hlbiBJIHNhaWQgdGhhdCB0aGUgbWVhbiBhbmQgbWVkaWFuIHZhbHVlcyBhcmUg4oCcbm90IG5lY2Vzc2FyaWx5IG9ic2VydmVkIGluIHRoZSBkYXRhLuKAnQoKSW4gdGhpcyBjYXNlLCB0aGUgbW9kZSB2YWx1ZSAoMTc2KSBpcyBhbHNvIGEgbW9yZSByZWFzb25hYmxlIGVzdGltYXRlIG9mIHRoZSBhdmVyYWdlIHBlcnNvbidzIHdlaWdodCB0aGFuIHRoZSBtZWFuLiBBbmQgdW5saWtlIHRoZSBtZWFuIGFuZCB0aGUgbWVkaWFuLCBwYXJ0aWNpcGFudHMgMTggYW5kIDE5IGFjdHVhbGx5IHdlaWdoIDE3NiBwb3VuZHMuIEknbSBfX25vdF9fIHNheWluZyB0aGF0IHRoZSBtb2RlIGlzIGFsd2F5cyB0aGUgYmVzdCBtZWFzdXJlIG9mIGNlbnRyYWwgdGVuZGVuY3kgdG8gdXNlLiBJIF9fYW1fXyBzYXlpbmcgdGhhdCB5b3UgY2FuIG9mdGVuIGxlYXJuIHVzZWZ1bCBpbmZvcm1hdGlvbiBmcm9tIHlvdXIgZGF0YSBieSBjYWxjdWxhdGluZyBhbmQgY29tcGFyaW5nIHRoZXNlIHJlbGF0aXZlbHkgc2ltcGxlIGRlc2NyaXB0aXZlIHN0YXRpc3RpY3Mgb24gZWFjaCBvZiB5b3VyIG51bWVyaWMgdmFyaWFibGVzLgo=