Topic 9B: Writing R Functions
Welcome to the ninth Data Science module computer lab.
By now, you will have gained some experience using a variety of functions in R, ranging from inbuilt ‘base R’ functions, to package-specific functions, such as those contained in the plotly
package.
In this computer lab we will extend our R coding skills and focus on writing functions in R.
By the end of this lab, you should understand the R code syntax required to create functions, and feel comfortable writing simple and intermediate functions. Let’s get started!
R Functions Overview
Usually, if you need to carry out a specific statistical process in R, there will already be a relevant function for you to use!
However, when you are conducting your own coding or research, sometimes it can be extremely beneficial to write your own custom functions. These can range from simple functions of a few lines of code, to extremely detailed, multi-layered functions that call other functions and packages. The creative possibilities are endless.
Recall that we briefly discussed writing R functions several weeks ago, in Section 4.2 of the Data Visualisation in R supplement.
Before you continue with this lab, please read over the R function content in this supplement, to refresh your memory. It may also be helpful to keep this content open in a separate tab while you work through the lab material.
Writing Simple Functions
Once you have reread the content in Section 4.2 of the Data Visualisation in R supplement, let’s try to write some simple functions in R. These functions actually already exist in base R, but this will be good practice for subsequent work.
Mean Function
Suppose that you would like to write an R function that computes the mean.
Recall that the mean \(\bar{x}\) of a set of \(n\) values \(x_1, x_2, \cdots, x_n\) is computed using the formula \[\bar{x} = \displaystyle \frac{\sum_{i=1}^n x_i}{n} .\]
There are several ways that we could write this function. We have made a head-start - take a look at the partially completed code below, and see if you can complete it by filling in the missing ...
parts.
mean_func <- function(values){
# Argument:
# values: This is our list of values
n <- length(values)
sum(...) / ...
}
Note that, based on this format, our function can compute the mean of a string of numbers of any non-zero length.
Verify that your mean_func
function works by computing the mean of the set of values 2, 3, 4, 5, 6, 7, 8
. If your function is correct, you should receive an answer of 5
.
Sample standard deviation Function
Now that you have had some practice writing R functions, let’s tackle a harder problem.
Recall the equation for the sample standard deviation \(s\) (introduced in Topic 2):
\[s = \displaystyle \sqrt{ \frac{1}{n-1} \sum_{i=1}^n (x_i - \bar{x})^2} .\]
Using your mean_func
function, and your code from 2.1 as a guide, write a function called sd_func
that computes the sample standard deviation of a set of \(n\) values \(x_1, x_2, \cdots, x_n\).
Hints:
- You can use the same input as for your
mean_func
function.
- When you are multiplying parts of a function together, you need to use the \(\ast\) symbol.
- You can use the built-in R function
sqrt()
to compute the square root.
- You can use your
mean_func
function within your new function, when computing the \(\bar{x}\) value.
- Be careful to include brackets to separate different parts of your calculations.
- Don’t forget the square term in the equation.
Verify that your sample standard deviation function works by computing the sample standard deviation of the set of values 2, 3, 4, 5, 6, 7, 8
. If your function is correct, you should receive an answer of 2.160247
.
Check how your function compares to the inbuilt R function sd
, which takes the one argument - the set of values. If your function is written correctly, you should obtain an identical result!
Writing a \(t\)-test Function
Now that we have had some practice writing R functions, let’s move on to a slightly harder scenario, and try to write an R function for computing a \(t\)-test.
Recall that in Topic 5 we introduced the \(t\)-test as a common type of hypothesis test.
The test statistic \(t\) used for the one-sample \(t\)-test has the following formula:
\[t = \displaystyle \frac{\bar{x} - \mu_0}{s / \sqrt{n}},\]
where \(\bar{x}\), \(s\) and \(n\) are the sample mean, sample standard deviation, and sample size respectively, as previously defined, and \(\mu_0\) denotes the mean under the null hypothesis.
Note that \(s / \sqrt{n}\) is the standard error of \(t\), and is often denoted by \(SE\).
\(t\)-test test statistic
Using your functions mean_func
and sd_func
, write a new R function called t_test_func
that computes the test statistic \(t\) for a one-sample \(t\)-test with an arbitrary null hypothesis mean \(\mu_0\).
Hints:
- Most of the hard work is already done, since you can use your two functions.
- You will need to provide one additional input argument.
To test your test statistic function, evaluate it for the set of values 2, 3, 4, 5, 6, 7, 8
, with the null hypothesis mean set to 4.
Compare your result to the result of the inbuilt R function t.test
, by running the code t.test(c(2:8), mu = 4)
. If you have written your function correctly, the t.test
\(t=\) output should be identical to the value of \(t\) computed by your t_test_func
function.
Hint: Don’t worry if your t_test_func
function gives a slightly different value - as long as it is correct to 3 or 4 decimal places that is fine. If it is very different, just go back over your function, and check for errors such as insufficient or misplaced brackets. It’s very easy to make mistakes like this.
Degrees of freedom
Recall that the degrees of freedom for a one-sample \(t\)-test is equal to \(n-1\). It would be good to include this information in your t_test_func
output.
As noted in Section 4.2.2 of the Data Visualisation in R supplement, if you want multiple results to be provided as the output of a function, you will need to combine these results into one output object.
There are several ways in which we can do this.
The simplest option is to use the combine function, c()
. For example, if your test statistic \(t\) result is stored in the object t.val
, and your degrees of freedom result is stored in the object df
, you could use c(t.val, df)
to output your results.
However, this won’t provide any extra information about your output - that might be fine if you are the only person using your function, but if you intend your function to be used by others, then adding more detail to the output might be desirable.
As a simple extra step, continuing the example above you could use c("test statistic value" = t.val, "degrees of freedom" = df)
to include some helpful additional details about your function output.
Using the information above, update your t_test_func
function so that it outputs the test statistic \(t\) and the degrees of freedom, with clear labels.
Test your updated function, using the inputs discussed in 3.2.
The concatenate Function
As a further improvement, rather than using e.g. c("test statistic value" = t.val, "degrees of freedom" = df)
, we could instead use the concatenate function cat
to provide more detailed output that is easier to read and interpret.
An example application of the cat
function is provided below:
cat("The test statistic is", round(t.val, 4))
This code will output a single character vector, e.g. The test statistic is 1.2247, which is nicer to read than the c
function method we used in 3.3.
Here, anything that is not an R code result is surrounded by quotation marks "..."
. A comma is used to separate sections of R code results and text (or text related code).
If we would like to include several statements in our output, we can use "\n"
to specify a new line. For example, the layout for two lines could be:
cat("Some text here", some R code here, "\n",
"some more text", some more R code)
Using the information above, incorporate the cat
function into your t_test_func
so that the output is in a clear, easy to read format.
Test your updated function, using the inputs discussed in 3.2.
Note: This brief introduction does not cover all aspects of the concatenate function.
\(p\)-value Function
Suppose that we would also like to compute the \(p\)-value that corresponds to the \(t\) test statistic. Recall from Topic 5 that for a two-sided hypothesis test, the \(p\)-value is equal to the probability that we observe a test statistic value as extreme, or more extreme, than the observed test statistic we have computed using our sample.
In other words, we have that \[\text{$p$-value} = 1 - P(-t \leq T \leq t) ,\] i.e. \[\text{$p$-value} = P(T \leq -t) + P(T \geq t) .\]
Recall that in section 2 of Computer Lab 4 we discussed the use of functions like dnorm
, pnorm
and qnorm
for computing the density, probability distribution function and quantile value respectively of normally distributed random variables.
Similarly, we can use the inbuilt R function pt
to compute the probability that the test statistic \(T\) (which follows a Student’s \(t\) distribution) takes a value less than or equal to an arbitrary value. We will however also need to include the degrees of freedom as an argument.
For example, if we would like to compute \(P(T \leq 5)\), and our degrees of freedom is 4, we can use the code pt(5, 4)
.
If we would like to compute \(P(T \geq 5)\), we can be clever and use the code 1 - pt(5, 4)
.
Based on this information, add extra code to your t_test_func
function so that it also provides (as a third output) the corresponding \(p\)-value for the test statistic.
Hint: Try to use the symmetry property of the Student’s \(t\)-distribution when including code for the \(p\)-value calculations in your function.
Run both your updated t_test_func
function and the inbuilt t.test
R function, using the inputs discussed in 3.2. Verify that your t_test_func
function \(p\)-value is correct, by comparing it to the output of the t.test
R function.
We now have written functions that, combined, can be used to conduct a one-sample \(t\)-test for a two-sided hypothesis. Well done!
LS0tDQp0aXRsZTogIlNUTTEwMDE6IENvbXB1dGVyIExhYiA5QiINCm91dHB1dDoNCiAgYm9va2Rvd246Omh0bWxfZG9jdW1lbnQyOiANCiAgICB0b2M6IHRydWUNCiAgICB0b2NfZmxvYXQ6IHRydWUNCiAgICBjb2RlX2Rvd25sb2FkOiB0cnVlDQogICAgdGhlbWU6IHJlYWRhYmxlDQogICAgY29kZV9mb2xkaW5nOiBzaG93DQpiaWJsaW9ncmFwaHk6IFNUTTEwMDFfRFNfQ0xfcmVmZXJlbmNlcy5iaWIgDQpsaW5rLWNpdGF0aW9uczogeWVzDQotLS0NCg0KPHN0eWxlPg0KI1RPQyB7DQogIGJhY2tncm91bmQ6IHVybCgiaHR0cHM6Ly93d3cubGF0cm9iZS5lZHUuYXUvX21lZGlhL2xhLXRyb2JlLWFwaS92NS9pbWcvbG9nby5zdmciKTsNCiAgYmFja2dyb3VuZC1zaXplOiBjb250YWluOw0KICBwYWRkaW5nLXRvcDogODBweCAhaW1wb3J0YW50Ow0KICBiYWNrZ3JvdW5kLXJlcGVhdDogbm8tcmVwZWF0Ow0KfQ0KPC9zdHlsZT4NCg0KIyMjIERhdGEgU2NpZW5jZSBNb2R1bGUgey19DQoNCiMjIyBUb3BpYyA5QjogV3JpdGluZyBSIEZ1bmN0aW9ucyB7LX0NCg0KPGJyPg0KDQpXZWxjb21lIHRvIHRoZSBuaW50aCBEYXRhIFNjaWVuY2UgbW9kdWxlIGNvbXB1dGVyIGxhYi4NCg0KQnkgbm93LCB5b3Ugd2lsbCBoYXZlIGdhaW5lZCBzb21lIGV4cGVyaWVuY2UgdXNpbmcgYSB2YXJpZXR5IG9mIGZ1bmN0aW9ucyBpbiBSLCByYW5naW5nIGZyb20gaW5idWlsdCAnYmFzZSBSJyBmdW5jdGlvbnMsIHRvIHBhY2thZ2Utc3BlY2lmaWMgZnVuY3Rpb25zLCBzdWNoIGFzIHRob3NlIGNvbnRhaW5lZCBpbiB0aGUgYHBsb3RseWAgcGFja2FnZS4NCg0KSW4gdGhpcyBjb21wdXRlciBsYWIgd2Ugd2lsbCBleHRlbmQgb3VyIFIgY29kaW5nIHNraWxscyBhbmQgZm9jdXMgb24gd3JpdGluZyBmdW5jdGlvbnMgaW4gUi4NCg0KQnkgdGhlIGVuZCBvZiB0aGlzIGxhYiwgeW91IHNob3VsZCB1bmRlcnN0YW5kIHRoZSBSIGNvZGUgc3ludGF4IHJlcXVpcmVkIHRvIGNyZWF0ZSBmdW5jdGlvbnMsIGFuZCBmZWVsIGNvbWZvcnRhYmxlIHdyaXRpbmcgc2ltcGxlIGFuZCBpbnRlcm1lZGlhdGUgZnVuY3Rpb25zLiBMZXQncyBnZXQgc3RhcnRlZCENCg0KIyBSIEZ1bmN0aW9ucyBPdmVydmlldw0KDQpVc3VhbGx5LCBpZiB5b3UgbmVlZCB0byBjYXJyeSBvdXQgYSBzcGVjaWZpYyBzdGF0aXN0aWNhbCBwcm9jZXNzIGluIFIsIHRoZXJlIHdpbGwgYWxyZWFkeSBiZSBhIHJlbGV2YW50IGZ1bmN0aW9uIGZvciB5b3UgdG8gdXNlIQ0KSG93ZXZlciwgd2hlbiB5b3UgYXJlIGNvbmR1Y3RpbmcgeW91ciBvd24gY29kaW5nIG9yIHJlc2VhcmNoLCBzb21ldGltZXMgaXQgY2FuIGJlIGV4dHJlbWVseSBiZW5lZmljaWFsIHRvIHdyaXRlIHlvdXIgb3duIGN1c3RvbSBmdW5jdGlvbnMuIFRoZXNlIGNhbiByYW5nZSBmcm9tIHNpbXBsZSBmdW5jdGlvbnMgb2YgYSBmZXcgbGluZXMgb2YgY29kZSwgdG8gZXh0cmVtZWx5IGRldGFpbGVkLCBtdWx0aS1sYXllcmVkIGZ1bmN0aW9ucyB0aGF0IGNhbGwgb3RoZXIgZnVuY3Rpb25zIGFuZCBwYWNrYWdlcy4gVGhlIGNyZWF0aXZlIHBvc3NpYmlsaXRpZXMgYXJlIGVuZGxlc3MuDQoNClJlY2FsbCB0aGF0IHdlIGJyaWVmbHkgZGlzY3Vzc2VkIHdyaXRpbmcgUiBmdW5jdGlvbnMgc2V2ZXJhbCB3ZWVrcyBhZ28sIGluIFtTZWN0aW9uIDQuMiBvZiB0aGUgRGF0YSBWaXN1YWxpc2F0aW9uIGluIFIgc3VwcGxlbWVudF0oaHR0cHM6Ly9ib29rZG93bi5vcmcvcmVoay9zdG0xMDAxX2RzbV90MV9kYXRhX3Zpc3VhbGlzYXRpb25faW5fci9yLWNvZGluZy10ZWNobmlxdWVzLmh0bWwjd3JpdGluZy1yLWZ1bmN0aW9ucy1jb21wdXRlci1sYWItNGIpLiANCg0KKipCZWZvcmUgeW91IGNvbnRpbnVlIHdpdGggdGhpcyBsYWIsIHBsZWFzZSByZWFkIG92ZXIgdGhlIFIgZnVuY3Rpb24gY29udGVudCBpbiB0aGlzIHN1cHBsZW1lbnQsIHRvIHJlZnJlc2ggeW91ciBtZW1vcnkuIEl0IG1heSBhbHNvIGJlIGhlbHBmdWwgdG8ga2VlcCB0aGlzIGNvbnRlbnQgb3BlbiBpbiBhIHNlcGFyYXRlIHRhYiB3aGlsZSB5b3Ugd29yayB0aHJvdWdoIHRoZSBsYWIgbWF0ZXJpYWwuKioNCg0KIyBXcml0aW5nIFNpbXBsZSBGdW5jdGlvbnMgeyN2YWx1ZXN9DQoNCk9uY2UgeW91IGhhdmUgcmVyZWFkIHRoZSBjb250ZW50IGluIFtTZWN0aW9uIDQuMiBvZiB0aGUgRGF0YSBWaXN1YWxpc2F0aW9uIGluIFIgc3VwcGxlbWVudF0oaHR0cHM6Ly9ib29rZG93bi5vcmcvcmVoay9zdG0xMDAxX2RzbV90MV9kYXRhX3Zpc3VhbGlzYXRpb25faW5fci9yLWNvZGluZy10ZWNobmlxdWVzLmh0bWwjd3JpdGluZy1yLWZ1bmN0aW9ucy1jb21wdXRlci1sYWItNGIpLCBsZXQncyB0cnkgdG8gd3JpdGUgc29tZSBzaW1wbGUgZnVuY3Rpb25zIGluIFIuIFRoZXNlIGZ1bmN0aW9ucyBhY3R1YWxseSBhbHJlYWR5IGV4aXN0IGluIGJhc2UgUiwgYnV0IHRoaXMgd2lsbCBiZSBnb29kIHByYWN0aWNlIGZvciBzdWJzZXF1ZW50IHdvcmsuDQoNCiMjIE1lYW4gRnVuY3Rpb24geyNtZWFuZnVuY30NCg0KU3VwcG9zZSB0aGF0IHlvdSB3b3VsZCBsaWtlIHRvIHdyaXRlIGFuIFIgZnVuY3Rpb24gdGhhdCBjb21wdXRlcyB0aGUgbWVhbi4gDQpSZWNhbGwgdGhhdCB0aGUgbWVhbiAkXGJhcnt4fSQgb2YgYSBzZXQgb2YgJG4kIHZhbHVlcyAkeF8xLCB4XzIsIFxjZG90cywgeF9uJCBpcyBjb21wdXRlZCB1c2luZyB0aGUgZm9ybXVsYSAkJFxiYXJ7eH0gPSBcZGlzcGxheXN0eWxlIFxmcmFje1xzdW1fe2k9MX1ebiB4X2l9e259IC4kJA0KDQpUaGVyZSBhcmUgc2V2ZXJhbCB3YXlzIHRoYXQgd2UgY291bGQgd3JpdGUgdGhpcyBmdW5jdGlvbi4gV2UgaGF2ZSBtYWRlIGEgaGVhZC1zdGFydCAtIHRha2UgYSBsb29rIGF0IHRoZSBwYXJ0aWFsbHkgY29tcGxldGVkIGNvZGUgYmVsb3csIGFuZCBzZWUgaWYgeW91IGNhbiBjb21wbGV0ZSBpdCBieSBmaWxsaW5nIGluIHRoZSBtaXNzaW5nIGAuLi5gIHBhcnRzLg0KDQpgYGB7ciBjbGFzcy5zb3VyY2UgPSAiZm9sZC1zaG93IiwgZXZhbCA9IEYsIGVjaG8gPSBULCB3YXJuaW5nID0gRiwgbWVzc2FnZSA9IEZ9DQptZWFuX2Z1bmMgPC0gZnVuY3Rpb24odmFsdWVzKXsNCiAgIyBBcmd1bWVudDoNCiAgIyB2YWx1ZXM6IFRoaXMgaXMgb3VyIGxpc3Qgb2YgdmFsdWVzDQogIA0KICBuIDwtIGxlbmd0aCh2YWx1ZXMpDQogIHN1bSguLi4pIC8gLi4uDQoNCn0gICAgICAgICAgICAgICAgICAgICAgDQpgYGANCg0KKk5vdGUgdGhhdCwgYmFzZWQgb24gdGhpcyBmb3JtYXQsIG91ciBmdW5jdGlvbiBjYW4gY29tcHV0ZSB0aGUgbWVhbiBvZiBhIHN0cmluZyBvZiBudW1iZXJzIG9mIGFueSBub24temVybyBsZW5ndGguKg0KDQojIyMNCg0KVmVyaWZ5IHRoYXQgeW91ciBgbWVhbl9mdW5jYCBmdW5jdGlvbiB3b3JrcyBieSBjb21wdXRpbmcgdGhlIG1lYW4gb2YgdGhlIHNldCBvZiB2YWx1ZXMgYDIsIDMsIDQsIDUsIDYsIDcsIDhgLiBJZiB5b3VyIGZ1bmN0aW9uIGlzIGNvcnJlY3QsIHlvdSBzaG91bGQgcmVjZWl2ZSBhbiBhbnN3ZXIgb2YgYDVgLg0KDQojIyBTYW1wbGUgc3RhbmRhcmQgZGV2aWF0aW9uIEZ1bmN0aW9uDQoNCk5vdyB0aGF0IHlvdSBoYXZlIGhhZCBzb21lIHByYWN0aWNlIHdyaXRpbmcgUiBmdW5jdGlvbnMsIGxldCdzIHRhY2tsZSBhIGhhcmRlciBwcm9ibGVtLg0KDQpSZWNhbGwgdGhlIGVxdWF0aW9uIGZvciB0aGUgc2FtcGxlIHN0YW5kYXJkIGRldmlhdGlvbiAkcyQgKGludHJvZHVjZWQgaW4gW1RvcGljIDJdKGh0dHBzOi8vYm9va2Rvd24ub3JnL2Ffc2hha2VyL1NUTTEwMDFfVG9waWNfMi8yLTEtdmFyaWFuY2UtYW5kLXN0YW5kYXJkLWRldmlhdGlvbi5odG1sKSk6DQoNCiQkcyA9IFxkaXNwbGF5c3R5bGUgXHNxcnR7IFxmcmFjezF9e24tMX0gXHN1bV97aT0xfV5uICh4X2kgLSBcYmFye3h9KV4yfSAuJCQNClVzaW5nIHlvdXIgYG1lYW5fZnVuY2AgZnVuY3Rpb24sIGFuZCB5b3VyIGNvZGUgZnJvbSBcQHJlZihtZWFuZnVuYykgYXMgYSBndWlkZSwgd3JpdGUgYSBmdW5jdGlvbiBjYWxsZWQgYHNkX2Z1bmNgIHRoYXQgY29tcHV0ZXMgdGhlICoqc2FtcGxlIHN0YW5kYXJkIGRldmlhdGlvbioqIG9mIGEgc2V0IG9mICRuJCB2YWx1ZXMgJHhfMSwgeF8yLCBcY2RvdHMsIHhfbiQuDQoNCipIaW50czoqIA0KDQoqICpZb3UgY2FuIHVzZSB0aGUgc2FtZSBpbnB1dCBhcyBmb3IgeW91ciBgbWVhbl9mdW5jYCBmdW5jdGlvbi4qDQoqICpXaGVuIHlvdSBhcmUgbXVsdGlwbHlpbmcgcGFydHMgb2YgYSBmdW5jdGlvbiB0b2dldGhlciwgeW91IG5lZWQgdG8gdXNlIHRoZSAkXGFzdCQgc3ltYm9sLioNCiogKllvdSBjYW4gdXNlIHRoZSBidWlsdC1pbiBSIGZ1bmN0aW9uIGBzcXJ0KClgIHRvIGNvbXB1dGUgdGhlIHNxdWFyZSByb290LioNCiogKllvdSBjYW4gdXNlIHlvdXIgYG1lYW5fZnVuY2AgZnVuY3Rpb24gd2l0aGluIHlvdXIgbmV3IGZ1bmN0aW9uLCB3aGVuIGNvbXB1dGluZyB0aGUgJFxiYXJ7eH0kIHZhbHVlLioNCiogKkJlIGNhcmVmdWwgdG8gaW5jbHVkZSBicmFja2V0cyB0byBzZXBhcmF0ZSBkaWZmZXJlbnQgcGFydHMgb2YgeW91ciBjYWxjdWxhdGlvbnMuKg0KKiAqRG9uJ3QgZm9yZ2V0IHRoZSBzcXVhcmUgdGVybSBpbiB0aGUgZXF1YXRpb24uKg0KDQoNCiMjIw0KDQpWZXJpZnkgdGhhdCB5b3VyIHNhbXBsZSBzdGFuZGFyZCBkZXZpYXRpb24gZnVuY3Rpb24gd29ya3MgYnkgY29tcHV0aW5nIHRoZSBzYW1wbGUgc3RhbmRhcmQgZGV2aWF0aW9uIG9mIHRoZSBzZXQgb2YgdmFsdWVzIGAyLCAzLCA0LCA1LCA2LCA3LCA4YC4gSWYgeW91ciBmdW5jdGlvbiBpcyBjb3JyZWN0LCB5b3Ugc2hvdWxkIHJlY2VpdmUgYW4gYW5zd2VyIG9mIGAyLjE2MDI0N2AuICANCg0KIyMjDQoNCkNoZWNrIGhvdyB5b3VyIGZ1bmN0aW9uIGNvbXBhcmVzIHRvIHRoZSBpbmJ1aWx0IFIgZnVuY3Rpb24gYHNkYCwgd2hpY2ggdGFrZXMgdGhlIG9uZSBhcmd1bWVudCAtIHRoZSBzZXQgb2YgdmFsdWVzLiBJZiB5b3VyIGZ1bmN0aW9uIGlzIHdyaXR0ZW4gY29ycmVjdGx5LCB5b3Ugc2hvdWxkIG9idGFpbiBhbiBpZGVudGljYWwgcmVzdWx0IQ0KDQojIFdyaXRpbmcgYSAkdCQtdGVzdCBGdW5jdGlvbiB7I3R0ZXN0ZnVuY30NCg0KTm93IHRoYXQgd2UgaGF2ZSBoYWQgc29tZSBwcmFjdGljZSB3cml0aW5nIFIgZnVuY3Rpb25zLCBsZXQncyBtb3ZlIG9uIHRvIGEgc2xpZ2h0bHkgaGFyZGVyIHNjZW5hcmlvLCBhbmQgdHJ5IHRvIHdyaXRlIGFuIFIgZnVuY3Rpb24gZm9yIGNvbXB1dGluZyBhICR0JC10ZXN0Lg0KDQpSZWNhbGwgdGhhdCBpbiBbVG9waWMgNV0oaHR0cHM6Ly9ib29rZG93bi5vcmcvY29udGVudC81MGEzMTc4ZC01NDMyLTQ0YTMtYmI1Yy0xNzE4Y2EzZTFmZTIvMS10ZGlzdC5odG1sKSB3ZSBpbnRyb2R1Y2VkIHRoZSAkdCQtdGVzdCBhcyBhIGNvbW1vbiB0eXBlIG9mIGh5cG90aGVzaXMgdGVzdC4NCg0KVGhlIHRlc3Qgc3RhdGlzdGljICR0JCB1c2VkIGZvciB0aGUgb25lLXNhbXBsZSAkdCQtdGVzdCBoYXMgdGhlIGZvbGxvd2luZyBmb3JtdWxhOg0KDQokJHQgPSBcZGlzcGxheXN0eWxlIFxmcmFje1xiYXJ7eH0gLSBcbXVfMH17cyAvIFxzcXJ0e259fSwkJA0Kd2hlcmUgJFxiYXJ7eH0kLCAkcyQgYW5kICRuJCBhcmUgdGhlIHNhbXBsZSBtZWFuLCBzYW1wbGUgc3RhbmRhcmQgZGV2aWF0aW9uLCBhbmQgc2FtcGxlIHNpemUgcmVzcGVjdGl2ZWx5LCBhcyBwcmV2aW91c2x5IGRlZmluZWQsIGFuZCAkXG11XzAkIGRlbm90ZXMgdGhlIG1lYW4gdW5kZXIgdGhlIG51bGwgaHlwb3RoZXNpcy4NCg0KKk5vdGUgdGhhdCAkcyAvIFxzcXJ0e259JCBpcyB0aGUgc3RhbmRhcmQgZXJyb3Igb2YgJHQkLCBhbmQgaXMgb2Z0ZW4gZGVub3RlZCBieSAkU0UkLioNCg0KIyMgJHQkLXRlc3QgdGVzdCBzdGF0aXN0aWMNCg0KVXNpbmcgeW91ciBmdW5jdGlvbnMgYG1lYW5fZnVuY2AgYW5kIGBzZF9mdW5jYCwgd3JpdGUgYSBuZXcgUiBmdW5jdGlvbiBjYWxsZWQgYHRfdGVzdF9mdW5jYCB0aGF0IGNvbXB1dGVzIHRoZSB0ZXN0IHN0YXRpc3RpYyAkdCQgZm9yIGEgb25lLXNhbXBsZSAkdCQtdGVzdCB3aXRoIGFuIGFyYml0cmFyeSBudWxsIGh5cG90aGVzaXMgbWVhbiAkXG11XzAkLg0KDQoqSGludHM6Kg0KDQoqICpNb3N0IG9mIHRoZSBoYXJkIHdvcmsgaXMgYWxyZWFkeSBkb25lLCBzaW5jZSB5b3UgY2FuIHVzZSB5b3VyIHR3byBmdW5jdGlvbnMuKg0KKiAqWW91IHdpbGwgbmVlZCB0byBwcm92aWRlIG9uZSBhZGRpdGlvbmFsIGlucHV0IGFyZ3VtZW50LioNCg0KIyMgeyN0dGVzdH0NCg0KVG8gdGVzdCB5b3VyIHRlc3Qgc3RhdGlzdGljIGZ1bmN0aW9uLCBldmFsdWF0ZSBpdCBmb3IgdGhlIHNldCBvZiB2YWx1ZXMgYDIsIDMsIDQsIDUsIDYsIDcsIDhgLCB3aXRoIHRoZSBudWxsIGh5cG90aGVzaXMgbWVhbiBzZXQgdG8gNC4NCg0KQ29tcGFyZSB5b3VyIHJlc3VsdCB0byB0aGUgcmVzdWx0IG9mIHRoZSBpbmJ1aWx0IFIgZnVuY3Rpb24gYHQudGVzdGAsIGJ5IHJ1bm5pbmcgdGhlIGNvZGUgYHQudGVzdChjKDI6OCksIG11ID0gNClgLiAgSWYgeW91IGhhdmUgd3JpdHRlbiB5b3VyIGZ1bmN0aW9uIGNvcnJlY3RseSwgdGhlIGB0LnRlc3RgICR0PSQgb3V0cHV0IHNob3VsZCBiZSBpZGVudGljYWwgdG8gdGhlIHZhbHVlIG9mICR0JCBjb21wdXRlZCBieSB5b3VyIGB0X3Rlc3RfZnVuY2AgZnVuY3Rpb24uDQoNCipIaW50OiBEb24ndCB3b3JyeSBpZiB5b3VyIGB0X3Rlc3RfZnVuY2AgZnVuY3Rpb24gZ2l2ZXMgYSBzbGlnaHRseSBkaWZmZXJlbnQgdmFsdWUgLSBhcyBsb25nIGFzIGl0IGlzIGNvcnJlY3QgdG8gMyBvciA0IGRlY2ltYWwgcGxhY2VzIHRoYXQgaXMgZmluZS4gSWYgaXQgaXMgdmVyeSBkaWZmZXJlbnQsIGp1c3QgZ28gYmFjayBvdmVyIHlvdXIgZnVuY3Rpb24sIGFuZCBjaGVjayBmb3IgZXJyb3JzIHN1Y2ggYXMgaW5zdWZmaWNpZW50IG9yIG1pc3BsYWNlZCBicmFja2V0cy4gSXQncyB2ZXJ5IGVhc3kgdG8gbWFrZSBtaXN0YWtlcyBsaWtlIHRoaXMuKg0KDQojIyBEZWdyZWVzIG9mIGZyZWVkb20geyNkZn0NCg0KUmVjYWxsIHRoYXQgdGhlIGRlZ3JlZXMgb2YgZnJlZWRvbSBmb3IgYSBvbmUtc2FtcGxlICR0JC10ZXN0IGlzIGVxdWFsIHRvICRuLTEkLiBJdCB3b3VsZCBiZSBnb29kIHRvIGluY2x1ZGUgdGhpcyBpbmZvcm1hdGlvbiBpbiB5b3VyIGB0X3Rlc3RfZnVuY2Agb3V0cHV0Lg0KDQpBcyBub3RlZCBpbiBbU2VjdGlvbiA0LjIuMiBvZiB0aGUgRGF0YSBWaXN1YWxpc2F0aW9uIGluIFIgc3VwcGxlbWVudF0oaHR0cHM6Ly9ib29rZG93bi5vcmcvcmVoay9zdG0xMDAxX2RzbV90MV9kYXRhX3Zpc3VhbGlzYXRpb25faW5fci9yLWNvZGluZy10ZWNobmlxdWVzLmh0bWwjd3JpdGluZy1yLWZ1bmN0aW9ucy1jb21wdXRlci1sYWItNGIpLCBpZiB5b3Ugd2FudCBtdWx0aXBsZSByZXN1bHRzIHRvIGJlIHByb3ZpZGVkIGFzIHRoZSBvdXRwdXQgb2YgYSBmdW5jdGlvbiwgeW91IHdpbGwgbmVlZCB0byBjb21iaW5lIHRoZXNlIHJlc3VsdHMgaW50byBvbmUgb3V0cHV0IG9iamVjdC4gDQoNClRoZXJlIGFyZSBzZXZlcmFsIHdheXMgaW4gd2hpY2ggd2UgY2FuIGRvIHRoaXMuDQpUaGUgc2ltcGxlc3Qgb3B0aW9uIGlzIHRvIHVzZSB0aGUgY29tYmluZSBmdW5jdGlvbiwgYGMoKWAuIEZvciBleGFtcGxlLCBpZiB5b3VyIHRlc3Qgc3RhdGlzdGljICR0JCByZXN1bHQgaXMgc3RvcmVkIGluIHRoZSBvYmplY3QgYHQudmFsYCwgYW5kIHlvdXIgZGVncmVlcyBvZiBmcmVlZG9tIHJlc3VsdCBpcyBzdG9yZWQgaW4gdGhlIG9iamVjdCBgZGZgLCB5b3UgY291bGQgdXNlIGBjKHQudmFsLCBkZilgIHRvIG91dHB1dCB5b3VyIHJlc3VsdHMuDQoNCiogSG93ZXZlciwgdGhpcyB3b24ndCBwcm92aWRlIGFueSBleHRyYSBpbmZvcm1hdGlvbiBhYm91dCB5b3VyIG91dHB1dCAtIHRoYXQgbWlnaHQgYmUgZmluZSBpZiB5b3UgYXJlIHRoZSBvbmx5IHBlcnNvbiB1c2luZyB5b3VyIGZ1bmN0aW9uLCBidXQgaWYgeW91IGludGVuZCB5b3VyIGZ1bmN0aW9uIHRvIGJlIHVzZWQgYnkgb3RoZXJzLCB0aGVuIGFkZGluZyBtb3JlIGRldGFpbCB0byB0aGUgb3V0cHV0IG1pZ2h0IGJlIGRlc2lyYWJsZS4NCg0KKiBBcyBhIHNpbXBsZSBleHRyYSBzdGVwLCBjb250aW51aW5nIHRoZSBleGFtcGxlIGFib3ZlIHlvdSBjb3VsZCB1c2UgYGMoInRlc3Qgc3RhdGlzdGljIHZhbHVlIiA9IHQudmFsLCAiZGVncmVlcyBvZiBmcmVlZG9tIiA9IGRmKWAgdG8gaW5jbHVkZSBzb21lIGhlbHBmdWwgYWRkaXRpb25hbCBkZXRhaWxzIGFib3V0IHlvdXIgZnVuY3Rpb24gb3V0cHV0Lg0KDQpVc2luZyB0aGUgaW5mb3JtYXRpb24gYWJvdmUsIHVwZGF0ZSB5b3VyIGB0X3Rlc3RfZnVuY2AgZnVuY3Rpb24gc28gdGhhdCBpdCBvdXRwdXRzIHRoZSB0ZXN0IHN0YXRpc3RpYyAkdCQgYW5kIHRoZSBkZWdyZWVzIG9mIGZyZWVkb20sIHdpdGggY2xlYXIgbGFiZWxzLg0KDQpUZXN0IHlvdXIgdXBkYXRlZCBmdW5jdGlvbiwgdXNpbmcgdGhlIGlucHV0cyBkaXNjdXNzZWQgaW4gXEByZWYodHRlc3QpLg0KDQojIyBUaGUgY29uY2F0ZW5hdGUgRnVuY3Rpb24NCg0KQXMgYSBmdXJ0aGVyIGltcHJvdmVtZW50LCByYXRoZXIgdGhhbiB1c2luZyBlLmcuIGBjKCJ0ZXN0IHN0YXRpc3RpYyB2YWx1ZSIgPSB0LnZhbCwgImRlZ3JlZXMgb2YgZnJlZWRvbSIgPSBkZilgLCB3ZSBjb3VsZCBpbnN0ZWFkIHVzZSB0aGUgY29uY2F0ZW5hdGUgZnVuY3Rpb24gYGNhdGAgdG8gcHJvdmlkZSBtb3JlIGRldGFpbGVkIG91dHB1dCB0aGF0IGlzIGVhc2llciB0byByZWFkIGFuZCBpbnRlcnByZXQuDQoNCkFuIGV4YW1wbGUgYXBwbGljYXRpb24gb2YgdGhlIGBjYXRgIGZ1bmN0aW9uIGlzIHByb3ZpZGVkIGJlbG93Og0KDQpgYGB7ciBjbGFzcy5zb3VyY2UgPSAiZm9sZC1zaG93IiwgZXZhbCA9IEYsIGVjaG8gPSBULCB3YXJuaW5nID0gRiwgbWVzc2FnZSA9IEZ9DQpjYXQoIlRoZSB0ZXN0IHN0YXRpc3RpYyBpcyIsIHJvdW5kKHQudmFsLCA0KSkNCmBgYA0KDQpUaGlzIGNvZGUgd2lsbCBvdXRwdXQgYSBzaW5nbGUgY2hhcmFjdGVyIHZlY3RvciwgZS5nLiAqVGhlIHRlc3Qgc3RhdGlzdGljIGlzIDEuMjI0NyosIHdoaWNoIGlzIG5pY2VyIHRvIHJlYWQgdGhhbiB0aGUgYGNgIGZ1bmN0aW9uIG1ldGhvZCB3ZSB1c2VkIGluIFxAcmVmKGRmKS4NCg0KSGVyZSwgYW55dGhpbmcgdGhhdCBpcyBub3QgYW4gUiBjb2RlIHJlc3VsdCBpcyBzdXJyb3VuZGVkIGJ5IHF1b3RhdGlvbiBtYXJrcyBgIi4uLiJgLiBBIGNvbW1hIGlzIHVzZWQgdG8gc2VwYXJhdGUgc2VjdGlvbnMgb2YgUiBjb2RlIHJlc3VsdHMgYW5kIHRleHQgKG9yIHRleHQgcmVsYXRlZCBjb2RlKS4NCg0KSWYgd2Ugd291bGQgbGlrZSB0byBpbmNsdWRlIHNldmVyYWwgc3RhdGVtZW50cyBpbiBvdXIgb3V0cHV0LCB3ZSBjYW4gdXNlIGAiXG4iYCB0byBzcGVjaWZ5IGEgbmV3IGxpbmUuIEZvciBleGFtcGxlLCB0aGUgbGF5b3V0IGZvciB0d28gbGluZXMgY291bGQgYmU6DQoNCmBgYHtyIGNsYXNzLnNvdXJjZSA9ICJmb2xkLXNob3ciLCBldmFsID0gRiwgZWNobyA9IFQsIHdhcm5pbmcgPSBGLCBtZXNzYWdlID0gRn0NCmNhdCgiU29tZSB0ZXh0IGhlcmUiLCBzb21lIFIgY29kZSBoZXJlLCAiXG4iLCANCiAgICAic29tZSBtb3JlIHRleHQiLCBzb21lIG1vcmUgUiBjb2RlKQ0KYGBgDQoNClVzaW5nIHRoZSBpbmZvcm1hdGlvbiBhYm92ZSwgaW5jb3Jwb3JhdGUgdGhlIGBjYXRgIGZ1bmN0aW9uIGludG8geW91ciBgdF90ZXN0X2Z1bmNgIHNvIHRoYXQgdGhlIG91dHB1dCBpcyBpbiBhIGNsZWFyLCBlYXN5IHRvIHJlYWQgZm9ybWF0Lg0KDQpUZXN0IHlvdXIgdXBkYXRlZCBmdW5jdGlvbiwgdXNpbmcgdGhlIGlucHV0cyBkaXNjdXNzZWQgaW4gXEByZWYodHRlc3QpLg0KDQoqTm90ZTogVGhpcyBicmllZiBpbnRyb2R1Y3Rpb24gZG9lcyBub3QgY292ZXIgYWxsIGFzcGVjdHMgb2YgdGhlIGNvbmNhdGVuYXRlIGZ1bmN0aW9uLioNCg0KIyMgJHAkLXZhbHVlIEZ1bmN0aW9uDQoNClN1cHBvc2UgdGhhdCB3ZSB3b3VsZCBhbHNvIGxpa2UgdG8gY29tcHV0ZSB0aGUgJHAkLXZhbHVlIHRoYXQgY29ycmVzcG9uZHMgdG8gdGhlICR0JCB0ZXN0IHN0YXRpc3RpYy4gUmVjYWxsIGZyb20gW1RvcGljIDVdKGh0dHBzOi8vYm9va2Rvd24ub3JnL2NvbnRlbnQvNTBhMzE3OGQtNTQzMi00NGEzLWJiNWMtMTcxOGNhM2UxZmUyLzMtMS1leHBsYWluaW5nLXRoZS1vbmUtc2FtcGxlLXQtdGVzdC1yZXN1bHRzLmh0bWwpIHRoYXQgZm9yIGEgdHdvLXNpZGVkIGh5cG90aGVzaXMgdGVzdCwgdGhlICRwJC12YWx1ZSBpcyBlcXVhbCB0byB0aGUgcHJvYmFiaWxpdHkgdGhhdCB3ZSBvYnNlcnZlIGEgdGVzdCBzdGF0aXN0aWMgdmFsdWUgYXMgZXh0cmVtZSwgb3IgbW9yZSBleHRyZW1lLCB0aGFuIHRoZSBvYnNlcnZlZCB0ZXN0IHN0YXRpc3RpYyB3ZSBoYXZlIGNvbXB1dGVkIHVzaW5nIG91ciBzYW1wbGUuIA0KDQpJbiBvdGhlciB3b3Jkcywgd2UgaGF2ZSB0aGF0ICQkXHRleHR7JHAkLXZhbHVlfSA9IDEgLSBQKC10IFxsZXEgVCBcbGVxIHQpICwkJCBpLmUuICQkXHRleHR7JHAkLXZhbHVlfSA9IFAoVCBcbGVxIC10KSArIFAoVCBcZ2VxIHQpIC4kJA0KDQpSZWNhbGwgdGhhdCBpbiBbc2VjdGlvbiAyIG9mIENvbXB1dGVyIExhYiA0XShodHRwczovL3JwdWJzLmNvbS9MVFVfU1RNMTAwMS9DTDQpIHdlIGRpc2N1c3NlZCB0aGUgdXNlIG9mIGZ1bmN0aW9ucyBsaWtlIGBkbm9ybWAsIGBwbm9ybWAgYW5kIGBxbm9ybWAgZm9yIGNvbXB1dGluZyB0aGUgZGVuc2l0eSwgcHJvYmFiaWxpdHkgZGlzdHJpYnV0aW9uIGZ1bmN0aW9uIGFuZCBxdWFudGlsZSB2YWx1ZSByZXNwZWN0aXZlbHkgb2Ygbm9ybWFsbHkgZGlzdHJpYnV0ZWQgcmFuZG9tIHZhcmlhYmxlcy4NCg0KU2ltaWxhcmx5LCB3ZSBjYW4gdXNlIHRoZSBpbmJ1aWx0IFIgZnVuY3Rpb24gYHB0YCB0byBjb21wdXRlIHRoZSBwcm9iYWJpbGl0eSB0aGF0IHRoZSB0ZXN0IHN0YXRpc3RpYyAkVCQgKHdoaWNoIGZvbGxvd3MgYSBTdHVkZW50J3MgJHQkIGRpc3RyaWJ1dGlvbikgdGFrZXMgYSB2YWx1ZSBsZXNzIHRoYW4gb3IgZXF1YWwgdG8gYW4gYXJiaXRyYXJ5IHZhbHVlLiBXZSB3aWxsIGhvd2V2ZXIgYWxzbyBuZWVkIHRvIGluY2x1ZGUgdGhlIGRlZ3JlZXMgb2YgZnJlZWRvbSBhcyBhbiBhcmd1bWVudC4gDQoNCkZvciBleGFtcGxlLCBpZiB3ZSB3b3VsZCBsaWtlIHRvIGNvbXB1dGUgJFAoVCBcbGVxIDUpJCwgYW5kIG91ciBkZWdyZWVzIG9mIGZyZWVkb20gaXMgNCwgd2UgY2FuIHVzZSB0aGUgY29kZSBgcHQoNSwgNClgLg0KSWYgd2Ugd291bGQgbGlrZSB0byBjb21wdXRlICRQKFQgXGdlcSA1KSQsIHdlIGNhbiBiZSBjbGV2ZXIgYW5kIHVzZSB0aGUgY29kZSBgMSAtIHB0KDUsIDQpYF5bc2luY2Ugd2UgYXJlIGRlYWxpbmcgd2l0aCBhIGNvbnRpbnVvdXMgcmFuZG9tIHZhcmlhYmxlXS4NCg0KQmFzZWQgb24gdGhpcyBpbmZvcm1hdGlvbiwgYWRkIGV4dHJhIGNvZGUgdG8geW91ciAgYHRfdGVzdF9mdW5jYCBmdW5jdGlvbiBzbyB0aGF0IGl0IGFsc28gcHJvdmlkZXMgKGFzIGEgdGhpcmQgb3V0cHV0KSB0aGUgY29ycmVzcG9uZGluZyAkcCQtdmFsdWUgZm9yIHRoZSB0ZXN0IHN0YXRpc3RpYy4NCg0KKkhpbnQ6IFRyeSB0byB1c2UgdGhlIHN5bW1ldHJ5IHByb3BlcnR5IG9mIHRoZSBTdHVkZW50J3MgJHQkLWRpc3RyaWJ1dGlvbiB3aGVuIGluY2x1ZGluZyBjb2RlIGZvciB0aGUgJHAkLXZhbHVlIGNhbGN1bGF0aW9ucyBpbiB5b3VyIGZ1bmN0aW9uLioNCg0KIyMjIA0KDQpSdW4gYm90aCB5b3VyIHVwZGF0ZWQgYHRfdGVzdF9mdW5jYCBmdW5jdGlvbiBhbmQgdGhlIGluYnVpbHQgYHQudGVzdGAgUiBmdW5jdGlvbiwgdXNpbmcgdGhlIGlucHV0cyBkaXNjdXNzZWQgaW4gXEByZWYodHRlc3QpLiBWZXJpZnkgdGhhdCB5b3VyIGB0X3Rlc3RfZnVuY2AgZnVuY3Rpb24gJHAkLXZhbHVlIGlzIGNvcnJlY3QsIGJ5ICBjb21wYXJpbmcgaXQgdG8gdGhlIG91dHB1dCBvZiB0aGUgYHQudGVzdGAgUiBmdW5jdGlvbi4NCg0KIyMjIyBXZSBub3cgaGF2ZSB3cml0dGVuIGZ1bmN0aW9ucyB0aGF0LCBjb21iaW5lZCwgY2FuIGJlIHVzZWQgdG8gY29uZHVjdCBhIG9uZS1zYW1wbGUgJHQkLXRlc3QgZm9yIGEgdHdvLXNpZGVkIGh5cG90aGVzaXMuIFdlbGwgZG9uZSEjIyMjDQoNCiMgRXh0ZW5zaW9uOiBBZGRpbmcgQWRkaXRpb25hbCBEZXRhaWxzIHRvIG91ciAkdCQtdGVzdCBGdW5jdGlvbg0KDQpUbyBmaW5pc2ggdXAgdGhpcyBjb21wdXRlciBsYWIsIHdlIGNvdWxkIGFkZCBzb21lIGFkZGl0aW9uYWwgZGV0YWlscyB0byBvdXIgZnVuY3Rpb24uIA0KDQojIw0KDQpBZGQgY29tbWVudHMgd2l0aCB0aGUgaGFzaCBzeW1ib2wgJFwjJCB3aXRoaW4geW91ciBgdF90ZXN0X2Z1bmNgIGZ1bmN0aW9uIHRvIGV4cGxhaW4gd2hhdCBlYWNoIGxpbmUgb2YgY29kZSBpcyBkb2luZyAtIGUuZy46DQoNCmBgYHtyIGNsYXNzLnNvdXJjZSA9ICJmb2xkLXNob3ciLCBldmFsID0gRiwgZWNobyA9IFQsIHdhcm5pbmcgPSBGLCBtZXNzYWdlID0gRn0NCnRfdGVzdF9mdW5jIDwtIGZ1bmN0aW9uKC4uLil7DQogIA0KICAuLi4NCiAgDQogICMgdGhpcyBjb2RlIGNvbXB1dGVzIHRoZSBkZWdyZWVzIG9mIGZyZWVkb20NCiAgLi4uDQogIA0KICAuLi4NCg0KfSAgIA0KYGBgDQoNCiMjDQoNClVzZSB0aGUgYGxpc3RgIGZ1bmN0aW9uIHRvIGZ1cnRoZXIgbW9kaWZ5IHlvdXIgYHRfdGVzdF9mdW5jYCBmdW5jdGlvbiBzbyB0aGF0IHlvdXIgb3V0cHV0IGlzIGEgYGxpc3RgLCB3aXRoIGVhY2ggb3V0cHV0IG5hbWVkIGFwcHJvcHJpYXRlbHkuIEFsc28gcHJvdmlkZSB0aGUgc2FtcGxlIG1lYW4gYXMgYSBmb3VydGggb3V0cHV0LiANCg0KWW91IGNhbiBwcm92aWRlIG5hbWVzIGZvciB0aGUgb2JqZWN0cyB5b3UgaW5jbHVkZSBpbiB5b3VyIGxpc3QgdmVyeSBlYXNpbHksIGUuZy4gYG1lYW4gPSBtZWFuLnZhbGAgd2lsbCBhc3NpZ24gdGhlIG5hbWUgYG1lYW5gIHRvIHlvdXIgYG1lYW4udmFsYCByZXN1bHQuDQoNCiMjIw0KDQpWZXJpZnkgeW91ciB1cGRhdGVkIGNvZGUgaXMgd29ya2luZyBhcyBpbnRlbmRlZCB1c2luZyB0aGUgaW5wdXRzIGRpc2N1c3NlZCBpbiBcQHJlZih0dGVzdCksIGFuZCB1c2UgdGhlIGBuYW1lc2AgZnVuY3Rpb24gdG8gY2hlY2sgd2hhdCByZXN1bHRzIGFyZSBhdmFpbGFibGUgZnJvbSB5b3VyIG5ldyBgdF90ZXN0X2Z1bmNgIG91dHB1dC4NCg0KVHJ5IHVzaW5nIHRoZSBgJGAgb3BlcmF0b3IgdG8gY2hlY2sgc3BlY2lmaWMgdmFsdWVzIGZyb20geW91ciBvdXRwdXQsIGUuZy4gYHQudGVzdC5vdXRwdXQkbWVhbmAuDQoNCjxicj4NCg0KIyMjIyBHcmVhdCBqb2IsIHRoYXQgY29uY2x1ZGVzIG91ciBSIGZ1bmN0aW9uIHdyaXRpbmcuICMjIyMgey19DQoNCkhvcGVmdWxseSB0aGlzIGxhYiBoYXMgZGVteXN0aWZpZWQgUiBmdW5jdGlvbiBjcmVhdGlvbiwgYW5kIHNob3duIHlvdSB0aGF0IHdyaXRpbmcgZnVuY3Rpb25zIGluIFIgaXMgcGVyaGFwcyBlYXNpZXIgdGhhbiB5b3UgbWlnaHQgaGF2ZSB0aG91Z2h0Lg0KDQo8YnI+DQoNCjxmb250IGNvbG9yID0gImdyZXkiPg0KVGhlc2Ugbm90ZXMgaGF2ZSBiZWVuIHByZXBhcmVkIGJ5IFJ1cGVydCBLdXZla2UgYW5kIEFtYW5kYSBTaGFrZXIuIFRoZSBjb3B5cmlnaHQgZm9yIHRoZSBtYXRlcmlhbCBpbiB0aGVzZSBub3RlcyByZXNpZGVzIHdpdGggdGhlIGF1dGhvcnMgbmFtZWQgYWJvdmUsIHdpdGggdGhlIERlcGFydG1lbnQgb2YgTWF0aGVtYXRpY2FsIGFuZCBQaHlzaWNhbCBTY2llbmNlcyBhbmQgd2l0aCBMYSBUcm9iZSBVbml2ZXJzaXR5LiBDb3B5cmlnaHQgaW4gdGhpcyB3b3JrIGlzIHZlc3RlZCBpbiBMYSBUcm9iZSBVbml2ZXJzaXR5IGluY2x1ZGluZyBhbGwgTGEgVHJvYmUgVW5pdmVyc2l0eSBicmFuZGluZyBhbmQgbmFtaW5nLiBVbmxlc3Mgb3RoZXJ3aXNlIHN0YXRlZCwgbWF0ZXJpYWwgd2l0aGluIHRoaXMgd29yayBpcyBsaWNlbnNlZCB1bmRlciBhIENyZWF0aXZlIENvbW1vbnMgQXR0cmlidXRpb24tTm9uIENvbW1lcmNpYWwtTm9uIERlcml2YXRpdmVzIExpY2Vuc2UgDQo8YSBocmVmID0gImh0dHBzOi8vY3JlYXRpdmVjb21tb25zLm9yZy9saWNlbnNlcy9ieS1uYy1uZC80LjAvQ0MiIHRhcmdldD0iX2JsYW5rIj4gQlktTkMtTkQuIDwvYT4NCjwvZm9udD4=