R notebook quick tutorial

Add a new chunk by clicking the Insert Chunk button on the toolbar or by pressing Ctrl+Alt+I.

When you save the notebook, an HTML file containing the code and output will be saved alongside it (click the Preview button or press Ctrl+Shift+K to preview the HTML file).

The preview shows you a rendered HTML copy of the contents of the editor. Consequently, unlike Knit, Preview does not run any R code chunks. Instead, the output of the chunk when it was last run in the editor is displayed.

Simulation Experiments

Simulation provides a straightforward way of approximating probabilities. One simulates a particular random experiment a large number of times, and a probability of an outcome is approximated by the relative frequency of the outcome in the repeated experiments.

The use of simulation experiments to better understand probability patterns is called the Monte Carlo method. We focus on two particular R functions that simplify the process of programming simulation experiments. The sample() function will take samples with or without replacement from a set, and the replicate() function is helpful in repeating a particular simulation experiment.

NOTE: remember to use the set.seed(2020) function in each code chunk in which you are generating random numbers to obtain the desired results.

Simulating a Game of Chance of coin tossing

Peter and Paul play a simple game involving repeated tosses of a fair coin. In a given toss, if heads is observed, Peter wins 1€ from Paul; otherwise if tails is tossed, Peter gives 1€ to Paul. If Peter starts with zero €, we are interested in his fortune as the game is played for 50 tosses.

We can simulate this game using the R sample() function. Peter’s winning on a particular toss will be 1€ or -1€ with equal probability. His winnings on 50 repeated tosses can be considered to be a sample of size 50 selected with replacement from the set {-1, 1}.

set.seed(2020)
FlipCoin <- function(n) sample(c(-1, 1), n, replace = TRUE)
flips = FlipCoin(50)
print(flips)
 [1]  1  1 -1  1  1 -1 -1  1  1  1 -1 -1  1  1  1  1  1  1  1 -1  1  1 -1  1 -1  1  1  1  1  1  1  1  1
[34] -1  1 -1  1  1  1 -1  1  1  1 -1  1  1  1 -1  1 -1

Desired Result

  [1]  1  1 -1  1  1 -1 -1  1  1  1 -1 -1  1  1  1  1  1  1  1 -1  1  1 -1  1 -1  1  1  1  1  1  1  1  1 -1  1 -1  1  1  1 -1  1  1  1 -1  1  1  1 -1  1 -1

In the outcome represented above, Peter lost the first two tosses, won the third, lost the fourth, and so on….

Exploring Cumulative Winnings

Suppose Peter is interested in his cumulative winnings as he plays this game. We store his individual toss winnings in the variable win. The function cumsum() will compute the cumulative winnings of the individual values and the cumulative values are stored in cum_win.

just adds the individual wins/losses up:

cum_win=cumsum(flips)
cum_win
 [1]  1  2  1  2  3  2  1  2  3  4  3  2  3  4  5  6  7  8  9  8  9 10  9 10  9 10 11 12 13 14 15 16 17
[34] 16 17 16 17 18 19 18 19 20 21 20 21 22 23 22 23 22

Desired Result

 [1]  1  2  1  2  3  2  1  2  3  4  3  2  3  4  5  6  7  8  9  8  9 10  9 10  9 10 11 12 13 14 15 16 17 16 17 16 17 18 19 18 19 20 21 20 21 22 23 22 23 22

We extend this and plot the sequence of cumulative winnings for 4 games. In order to plot the cumulative winnings we will use a line plot, this kind of plots can be printed using the plot() function, including the cumulative winnings as the vector we want to represent and type = "l" as a parameter to obtain a line plot.

# Extend this and plot the sequence of cumulative winnings for 4 games

set.seed(2020)
par(mfrow=c(2,2))
matrix4games <- data.frame(replicate(4,FlipCoin(50)))
colnames(matrix4games) <- c('First', 'Second', 'Third','Fourth')
for (col in names(matrix4games)){
  plot(apply(matrix4games[col],2,cumsum),type = "l",main = col,xlab="Index",
        ylab="cumsum(win)",)
  abline(h=0, col="black")
}

Desired Result Below you can see the four plots you have to obtain:

Is evidently much variability. How do we address:

    1. What is prob of Peter breaking even after 50 games?
    1. How often Peter will be on the lead?
    1. What will be value of Peter’s fortune?

First question can be calculated exactly, can only approximate 2) and 3) using Monte Carlo methods

R Function to Implement a Monte Carlo Experiment

One can obtain approximate answers to these questions by a Monte Carlo methods. In this type of experiment, we will simulate the random process and computes the statistic or statistics of interest. By repeating the random process many times, one obtains a collection of the statistics. We will then use the collection to approximate probabilities or expectations that answer the questions.

Let’s first consider Peter’s fortune F at the end of the game (question 3). We write a function peter_paul that simulates the fortunes for the 50 tosses and computes F that is equal to the sum of the individual fortunes. To make this function more general, we define n to be the number of tosses and let the default value of n be 50.

# Write a peter_paul function that simulates the fortunes for 50 tosses and calculate F, 
# that is the sum of the individual fortunes
set.seed(2020)
peter_paul=function(n=50){
  flip=FlipCoin(n)
  sum(flip)
}
F = peter_paul()
F
[1] 22

Desired Result

 [1] 22

In this game, Peter finished with a fortune of 22€. To repeat this for 1000 games, we use the replicate function with arguments 1000, the number of games, and the name of the function peter_paul() to repeat. The output of replicate is the vector of fortunes for the 1000 games that we assign to the variable F. Print the header of variable F to see the result.

# Use the replicate() function to repeat the experiment 1000 times
set.seed(2020)
F=replicate(1000, peter_paul())
F[1:6]
[1] 22 -2  2 -8 -2 -4

Desired Result

[1] 22 -2  2 -8 -2 -4

Summarizing the Monte Carlo Results

Since Peter’s fortune is integer-valued, a convenient way of summarizing the collection of values of F using table().

# Use the function table() to sumarize the values of F
table(F)
F
-22 -20 -18 -16 -14 -12 -10  -8  -6  -4  -2   0   2   4   6   8  10  12  14  16  18  20  22 
  4   3   5  15   8  31  31  69  72 111 100 120  95 109  79  61  36  20  14   5   5   6   1 

Desired Result

F
-22 -20 -18 -16 -14 -12 -10  -8  -6  -4  -2   0   2   4   6   8  10  12  14  16  18  20  22 
  4   3   5  15   8  31  31  69  72 111 100 120  95 109  79  61  36  20  14   5   5   6   1 

We can display the frequencies of F using plot() applied to the table output, and see what will be the value of Peter’s best fortune (question 3)

# Plot
plot(table(F))

Desired Result Below you can see the plot you have to obtain:

What is Peter’s chance of breaking even? (question 1) It is the ratio (approximated) of Peter finishing with 0 out of the 1000

But that answer can also be determined exactly. He breaks even if there are exactly n/2 heads in a binomial experiment of n trials with a probability of success equal to 0.50. So if n = 50 we can apply the dbinom function to obtain the exact value:

# Compute P(X=25) for X Binomial(n=50,p=0.5)
?dbinom
dbinom(25, 50, 0.5)
[1] 0.1122752
print(120/1000)
[1] 0.12

Note how close this exact number is to our approximated answer from the Monte Carlo simulation, that should be 120/1000 = 0.12

Modifying the Experiment to Learn About

New Statistics

We can add additional lines of code to our function peter_paul to compute several statistics of interest in our experiment. To answer our questions, we focus on the final fortune F, the number of times Peter is in the lead L, and the maximum cumulative winning M.

In the function, we define the vector of cumulative winnings cum_win. Here the output of the function is a vector consisting of the values of F, L, and M.

By naming the components (using, for example, F=sum(win)), we get more attractive output.

# Create a function to obtain the final fortune (F), the number of times Peter is in the lead (L) and 
# the maximum cumulative winning (W)
set.seed(2020)
peter_paul=function(n=50){
  flip=FlipCoin(n)
  cum_win=cumsum(flip)
  c(F =sum(flip), L=sum(cum_win>0),  M=max(cum_win))    
}

We simulate the game once

peter_paul()
 F  L  M 
22 50 23 

Desired Result

 F  L  M 
22 50 23 

In this game (with seed = 2020), Peter’s final fortune was 22€, he was in the lead for 50 plays, and his maximum total winning during the game was 23€. To repeat this game 1000 times, we again use replicate and store output in variable S.

Since the output of peter_paul is a vector, S will be a matrix of 3 rows and 1000 columns, where the rows correspond to the simulated draws of F, L, and M. We can verify the dimension of the matrix of S using the dim() function.

# Simulate 1000 times
set.seed(2020)
peter_paul=function(n=50){
  flip=FlipCoin(n)
  cum_win=cumsum(flip)
  c(F =sum(flip), L=sum(cum_win>0),  M=max(cum_win))    
}
S = replicate(1000,peter_paul())
dim(S)
[1]    3 1000

Desired Result

[1]    3 1000

How many times Peter in the lead? The likely answer to this question (we do not know for sure) is in the L row. Create the times_in_lead variable with all the values and print the header

# How many times is Peter in lead?
times_in_lead = S["L",]
times_in_lead[1:6]
[1] 50  4 47 17 17  9

Desired Result

[1] 50  4 47 17 17  9

We tabulate the simulated values using the table function, and the prop.table function will find the corresponding relative frequencies. We plot the result in a line graph:

# plot

plot(prop.table(table(times_in_lead)))

Desired Result Below you can see the plot you have to obtain:

The pattern of this graph for L isn’t what most people expect. Since it is a fair game, one might think it would be most likely for Peter to be ahead in 25 out of 50 tosses.

Instead, the most likely values are the extreme values L = 0,1,50, and the remaining values of L appear equally likely. So actually it is relatively common for Peter to always be losing or always winning in this game.

Last, let’s consider the distribution of M, Peter’s maximum winning during the 50 plays. We store the 1000 simulated values of M in the variable maximum_lead, tablulate the values using table(), and then plot them

# Plot the Maximum winings in all the experiments
maximum_lead = S["M",]
#maximum_lead[1:6]
plot(prop.table(table(maximum_lead)))

Desired Result Below you can see the plot you have to obtain:

From the plot, we can see that it is most likely for Peter to have maximum winnings of 1 or 2, but values between 3 and 6 are all relatively likely.

To compute the approximate probability that Peter will have a maximum winning of 10 or more, we find the number of values of maximum_lead that are 10 or greater and divide this sum by the number of simulation iterations 1000:

# What is the number of times Peter have a maximum lead greater than 10?
sum(maximum_lead >= 10)/1000
[1] 0.158

Since this probability is only about 16%, it is relatively rare for Peter to have a maximum winning of 10 or more.

Apply what you have learned

If you have managed to get this far you have secured 80% of the grade.

In this last section of the lab you will have to be creative and generate new questions about the coin game that you can later solve using R-simulation experiments.

Generate at least one new question and solve it.

Question:

What are the probabilities of Peter to win consecutively for 50 tosses ?

To demonstrate the idea, we will toss the coin only for 10 times in only 1 game

set.seed(2020)
flip=FlipCoin(10)
flip
 [1]  1  1 -1  1  1 -1 -1  1  1  1

We can count the consecutive by using function rle. Here lengths represent number of sequence of both -1 and 1. For example:

flip_st<-rle(flip)
table(flip_st)
       values
lengths -1 1
      1  1 0
      2  1 2
      3  0 1

Now we will calculate first the consecutive winning and sum the rest and assign zero value to show events without consecutive winning

win2con = flip_st$lengths[which(flip_st$values==1)]
other_win2con = flip_st$lengths[which(flip_st$values== - 1)]
other_win2con = sum(other_win2con)
other_win2con
[1] 3
win2con
[1] 2 2 3
el_win1orlost = 10 - sum(win2con)
el_win1orlost
[1] 3

We will need to create entries for event oof no consecutive win or losses and assign this variable win1orlost. We use function rep to add zero for those event. We combine both win2more and win1orlost and assign new table final_tab

win1orlost<- rep(0,el_win1orlost)
final_tab = append(win2con,win1orlost)
final_tab
[1] 2 2 3 0 0 0

Now let us calculate the frequencies of the consecutive winning and have the probability table

table (final_tab)
final_tab
0 2 3 
3 2 1 
prop.table(table (final_tab))
final_tab
        0         2         3 
0.5000000 0.3333333 0.1666667 

By tossing the coin by 10 times, we know that we going to have 3 consecutive win only once, 2 consecutive win twice and others which consist of only winning once or lost by thrice.

Let us put this in a **function and run 50 tossing coins and replicate this 1000 times to get the distribution probability of consecutive winning.

# Write your code here
set.seed(2020)
peter_paul_streak <- function(n=50){
  
  flip=FlipCoin(n)
  flip_st<-rle(flip)
  win2con = flip_st$lengths[which(flip_st$values==1)]
  other_win2con = flip_st$lengths[which(flip_st$values== - 1)]
  other_win2con = sum(other_win2con)
  el_win1orlost = n - sum(win2con)
  ##assigned zero for other condition (no consecutive win or lost)
  win1orlost<- rep(0,el_win1orlost)
  final_tab = append(win2con,win1orlost)
  
  
}
# We use Monte Carlo approach by running the function 1000 times for 50 
Winning2More = replicate(1000,peter_paul_streak(50))
# we do unlist to handle different size of list from the consecutive result
table(unlist(Winning2More))

    0     1     2     3     4     5     6     7     8     9    10    11    12 
25102  6462  3188  1564   767   350   191    97    47    16    16     3     1 

Table above shows the frequency of the winning in sequence from 1 to 12, meanwhile, zero represent no consecutive win or lost. We can see that the number of maximum 12 consecutive win which happen only once. And we can see that the number of consecutive winning is decreasing

round(prop.table(table(unlist(Winning2More))),3)

    0     1     2     3     4     5     6     7     8     9    10    11    12 
0.664 0.171 0.084 0.041 0.020 0.009 0.005 0.003 0.001 0.000 0.000 0.000 0.000 

Table here shows the probability of the consecutive winning. We can see that the probability of winning 6 or more consecutively is very low (below 0.00)

plot(prop.table(table(unlist(Winning2More))), main = paste("Probability of Winning in Sequence"),xlab="Frequencies",
ylab="probability")

Here is the chart

LS0tDQp0aXRsZTogIkNvaW4gR2FtZSBMYWIiDQphdXRob3I6ICJUZWFtIEIgTGFiIDEiDQpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sNCi0tLQ0KDQojIFIgbm90ZWJvb2sgcXVpY2sgdHV0b3JpYWwNCg0KDQpBZGQgYSBuZXcgY2h1bmsgYnkgY2xpY2tpbmcgdGhlICpJbnNlcnQgQ2h1bmsqIGJ1dHRvbiBvbiB0aGUgdG9vbGJhciBvciBieSBwcmVzc2luZyAqQ3RybCtBbHQrSSouDQoNCldoZW4geW91IHNhdmUgdGhlIG5vdGVib29rLCBhbiBIVE1MIGZpbGUgY29udGFpbmluZyB0aGUgY29kZSBhbmQgb3V0cHV0IHdpbGwgYmUgc2F2ZWQgYWxvbmdzaWRlIGl0IChjbGljayB0aGUgKlByZXZpZXcqIGJ1dHRvbiBvciBwcmVzcyAqQ3RybCtTaGlmdCtLKiB0byBwcmV2aWV3IHRoZSBIVE1MIGZpbGUpLg0KDQpUaGUgcHJldmlldyBzaG93cyB5b3UgYSByZW5kZXJlZCBIVE1MIGNvcHkgb2YgdGhlIGNvbnRlbnRzIG9mIHRoZSBlZGl0b3IuIENvbnNlcXVlbnRseSwgdW5saWtlICpLbml0KiwgKlByZXZpZXcqIGRvZXMgbm90IHJ1biBhbnkgUiBjb2RlIGNodW5rcy4gSW5zdGVhZCwgdGhlIG91dHB1dCBvZiB0aGUgY2h1bmsgd2hlbiBpdCB3YXMgbGFzdCBydW4gaW4gdGhlIGVkaXRvciBpcyBkaXNwbGF5ZWQuDQoNCiMgU2ltdWxhdGlvbiBFeHBlcmltZW50cyANCg0KU2ltdWxhdGlvbiBwcm92aWRlcyBhIHN0cmFpZ2h0Zm9yd2FyZCB3YXkgb2YgYXBwcm94aW1hdGluZyBwcm9iYWJpbGl0aWVzLiBPbmUgc2ltdWxhdGVzIGEgcGFydGljdWxhciByYW5kb20gZXhwZXJpbWVudCBhIGxhcmdlIG51bWJlciBvZiB0aW1lcywgYW5kIGEgcHJvYmFiaWxpdHkgb2YgYW4gb3V0Y29tZSBpcyBhcHByb3hpbWF0ZWQgYnkgdGhlIHJlbGF0aXZlIGZyZXF1ZW5jeSBvZiB0aGUgb3V0Y29tZSBpbiB0aGUgcmVwZWF0ZWQgZXhwZXJpbWVudHMuIA0KDQpUaGUgdXNlIG9mIHNpbXVsYXRpb24gZXhwZXJpbWVudHMgdG8gYmV0dGVyIHVuZGVyc3RhbmQgcHJvYmFiaWxpdHkgcGF0dGVybnMgaXMgY2FsbGVkIHRoZSBNb250ZSBDYXJsbyBtZXRob2QuIFdlIGZvY3VzIG9uIHR3byBwYXJ0aWN1bGFyIFIgZnVuY3Rpb25zIHRoYXQgc2ltcGxpZnkgdGhlIHByb2Nlc3Mgb2YgcHJvZ3JhbW1pbmcgc2ltdWxhdGlvbiBleHBlcmltZW50cy4gVGhlIGBzYW1wbGUoKWAgZnVuY3Rpb24gd2lsbCB0YWtlIHNhbXBsZXMgd2l0aCBvciB3aXRob3V0IHJlcGxhY2VtZW50IGZyb20gYSBzZXQsIGFuZCB0aGUgYHJlcGxpY2F0ZSgpYCBmdW5jdGlvbiBpcyBoZWxwZnVsIGluIHJlcGVhdGluZyBhIHBhcnRpY3VsYXIgc2ltdWxhdGlvbiBleHBlcmltZW50LiANCg0KTk9URTogcmVtZW1iZXIgdG8gdXNlIHRoZSBgc2V0LnNlZWQoMjAyMClgIGZ1bmN0aW9uIGluIGVhY2ggY29kZSBjaHVuayBpbiB3aGljaCB5b3UgYXJlIGdlbmVyYXRpbmcgcmFuZG9tIG51bWJlcnMgdG8gb2J0YWluIHRoZSBkZXNpcmVkIHJlc3VsdHMuDQoNCiMjIFNpbXVsYXRpbmcgYSBHYW1lIG9mIENoYW5jZSBvZiBjb2luIHRvc3NpbmcNCg0KUGV0ZXIgYW5kIFBhdWwgcGxheSBhIHNpbXBsZSBnYW1lIGludm9sdmluZyByZXBlYXRlZCB0b3NzZXMgb2YgYSBmYWlyIGNvaW4uIEluIGEgZ2l2ZW4gdG9zcywgaWYgaGVhZHMgaXMgb2JzZXJ2ZWQsIFBldGVyIHdpbnMgMeKCrCBmcm9tIFBhdWw7IG90aGVyd2lzZSBpZiB0YWlscyBpcyB0b3NzZWQsIFBldGVyIGdpdmVzIDHigqwgdG8gUGF1bC4gSWYgUGV0ZXIgc3RhcnRzIHdpdGggemVybyDigqwsIHdlIGFyZSBpbnRlcmVzdGVkIGluIGhpcyBmb3J0dW5lIGFzIHRoZSBnYW1lIGlzIHBsYXllZCBmb3IgNTAgdG9zc2VzLg0KDQpXZSBjYW4gc2ltdWxhdGUgdGhpcyBnYW1lIHVzaW5nIHRoZSBSIHNhbXBsZSgpIGZ1bmN0aW9uLiBQZXRlcidzIHdpbm5pbmcgb24gYSBwYXJ0aWN1bGFyIHRvc3Mgd2lsbCBiZSAx4oKsIG9yIC0x4oKsIHdpdGggZXF1YWwgcHJvYmFiaWxpdHkuIEhpcyB3aW5uaW5ncyBvbiA1MCByZXBlYXRlZCB0b3NzZXMgY2FuIGJlIGNvbnNpZGVyZWQgdG8gYmUgYSBzYW1wbGUgb2Ygc2l6ZSA1MCBzZWxlY3RlZCB3aXRoIHJlcGxhY2VtZW50IGZyb20gdGhlIHNldCB7LTEsIDF9Lg0KDQpgYGB7cn0NCnNldC5zZWVkKDIwMjApDQpGbGlwQ29pbiA8LSBmdW5jdGlvbihuKSBzYW1wbGUoYygtMSwgMSksIG4sIHJlcGxhY2UgPSBUUlVFKQ0KZmxpcHMgPSBGbGlwQ29pbig1MCkNCnByaW50KGZsaXBzKQ0KYGBgDQoNCioqRGVzaXJlZCBSZXN1bHQqKg0KYGBgDQogIFsxXSAgMSAgMSAtMSAgMSAgMSAtMSAtMSAgMSAgMSAgMSAtMSAtMSAgMSAgMSAgMSAgMSAgMSAgMSAgMSAtMSAgMSAgMSAtMSAgMSAtMSAgMSAgMSAgMSAgMSAgMSAgMSAgMSAgMSAtMSAgMSAtMSAgMSAgMSAgMSAtMSAgMSAgMSAgMSAtMSAgMSAgMSAgMSAtMSAgMSAtMQ0KYGBgDQoNCkluIHRoZSBvdXRjb21lIHJlcHJlc2VudGVkIGFib3ZlLCBQZXRlciBsb3N0IHRoZSBmaXJzdCB0d28gdG9zc2VzLCB3b24gdGhlIHRoaXJkLCBsb3N0IHRoZSBmb3VydGgsIGFuZCBzbyBvbi4uLi4NCg0KIyMgRXhwbG9yaW5nIEN1bXVsYXRpdmUgV2lubmluZ3MNCg0KU3VwcG9zZSBQZXRlciBpcyBpbnRlcmVzdGVkIGluIGhpcyBjdW11bGF0aXZlIHdpbm5pbmdzIGFzIGhlIHBsYXlzIHRoaXMgZ2FtZS4gV2Ugc3RvcmUgaGlzIGluZGl2aWR1YWwgdG9zcyB3aW5uaW5ncyBpbiB0aGUgdmFyaWFibGUgd2luLiBUaGUgZnVuY3Rpb24gYGN1bXN1bSgpYCB3aWxsIGNvbXB1dGUgdGhlIGN1bXVsYXRpdmUgd2lubmluZ3Mgb2YgdGhlIGluZGl2aWR1YWwgdmFsdWVzIGFuZCB0aGUgY3VtdWxhdGl2ZSB2YWx1ZXMgYXJlIHN0b3JlZCBpbiBgY3VtX3dpbi5gDQoNCmp1c3QgYWRkcyB0aGUgaW5kaXZpZHVhbCB3aW5zL2xvc3NlcyB1cDoNCg0KYGBge3J9DQpjdW1fd2luPWN1bXN1bShmbGlwcykNCmN1bV93aW4NCmBgYA0KDQoqKkRlc2lyZWQgUmVzdWx0KioNCmBgYA0KIFsxXSAgMSAgMiAgMSAgMiAgMyAgMiAgMSAgMiAgMyAgNCAgMyAgMiAgMyAgNCAgNSAgNiAgNyAgOCAgOSAgOCAgOSAxMCAgOSAxMCAgOSAxMCAxMSAxMiAxMyAxNCAxNSAxNiAxNyAxNiAxNyAxNiAxNyAxOCAxOSAxOCAxOSAyMCAyMSAyMCAyMSAyMiAyMyAyMiAyMyAyMg0KYGBgDQoNCldlIGV4dGVuZCB0aGlzIGFuZCBwbG90IHRoZSBzZXF1ZW5jZSBvZiBjdW11bGF0aXZlIHdpbm5pbmdzIGZvciA0IGdhbWVzLiBJbiBvcmRlciB0byBwbG90IHRoZSBjdW11bGF0aXZlIHdpbm5pbmdzIHdlIHdpbGwgdXNlIGEgbGluZSBwbG90LCB0aGlzIGtpbmQgb2YgcGxvdHMgY2FuIGJlIHByaW50ZWQgdXNpbmcgdGhlIGBwbG90KClgIGZ1bmN0aW9uLCBpbmNsdWRpbmcgdGhlIGN1bXVsYXRpdmUgd2lubmluZ3MgYXMgdGhlIHZlY3RvciB3ZSB3YW50IHRvIHJlcHJlc2VudCBhbmQgYHR5cGUgPSAibCJgIGFzIGEgcGFyYW1ldGVyIHRvIG9idGFpbiBhIGxpbmUgcGxvdC4NCg0KYGBge3J9DQojIEV4dGVuZCB0aGlzIGFuZCBwbG90IHRoZSBzZXF1ZW5jZSBvZiBjdW11bGF0aXZlIHdpbm5pbmdzIGZvciA0IGdhbWVzDQoNCnNldC5zZWVkKDIwMjApDQpwYXIobWZyb3c9YygyLDIpKQ0KbWF0cml4NGdhbWVzIDwtIGRhdGEuZnJhbWUocmVwbGljYXRlKDQsRmxpcENvaW4oNTApKSkNCmNvbG5hbWVzKG1hdHJpeDRnYW1lcykgPC0gYygnRmlyc3QnLCAnU2Vjb25kJywgJ1RoaXJkJywnRm91cnRoJykNCmZvciAoY29sIGluIG5hbWVzKG1hdHJpeDRnYW1lcykpew0KICBwbG90KGFwcGx5KG1hdHJpeDRnYW1lc1tjb2xdLDIsY3Vtc3VtKSx0eXBlID0gImwiLG1haW4gPSBjb2wseGxhYj0iSW5kZXgiLA0KICAgICAgICB5bGFiPSJjdW1zdW0od2luKSIsKQ0KICBhYmxpbmUoaD0wLCBjb2w9ImJsYWNrIikNCn0NCmBgYA0KDQoqKkRlc2lyZWQgUmVzdWx0KioNCkJlbG93IHlvdSBjYW4gc2VlIHRoZSBmb3VyIHBsb3RzIHlvdSBoYXZlIHRvIG9idGFpbjoNCjxwIGFsaWduPSJjZW50ZXIiPg0KICA8aW1nIHNyYz0iRmlndXJlcy9wbG90MS5wbmciIHdpZHRoPSI2MDAiPg0KPC9wPiANCg0KSXMgZXZpZGVudGx5IG11Y2ggdmFyaWFiaWxpdHkuIEhvdyBkbyB3ZSBhZGRyZXNzOg0KDQotIDEpIFdoYXQgaXMgcHJvYiBvZiBQZXRlciBicmVha2luZyBldmVuIGFmdGVyIDUwIGdhbWVzPw0KLSAyKSBIb3cgb2Z0ZW4gUGV0ZXIgd2lsbCBiZSBvbiB0aGUgbGVhZD8NCi0gMykgV2hhdCB3aWxsIGJlIHZhbHVlIG9mIFBldGVyJ3MgZm9ydHVuZT8NCg0KRmlyc3QgcXVlc3Rpb24gY2FuIGJlIGNhbGN1bGF0ZWQgZXhhY3RseSwgY2FuIG9ubHkgYXBwcm94aW1hdGUgMikgYW5kIDMpIHVzaW5nIE1vbnRlIENhcmxvIG1ldGhvZHMNCg0KIyMgUiBGdW5jdGlvbiB0byBJbXBsZW1lbnQgYSBNb250ZSBDYXJsbyBFeHBlcmltZW50DQoNCk9uZSBjYW4gb2J0YWluIGFwcHJveGltYXRlIGFuc3dlcnMgdG8gdGhlc2UgcXVlc3Rpb25zIGJ5IGEgTW9udGUgQ2FybG8gbWV0aG9kcy4gSW4gdGhpcyB0eXBlIG9mIGV4cGVyaW1lbnQsIHdlIHdpbGwgc2ltdWxhdGUgdGhlIHJhbmRvbSBwcm9jZXNzIGFuZCBjb21wdXRlcyB0aGUgc3RhdGlzdGljIG9yIHN0YXRpc3RpY3Mgb2YgaW50ZXJlc3QuIEJ5IHJlcGVhdGluZyB0aGUgcmFuZG9tIHByb2Nlc3MgbWFueSB0aW1lcywgb25lIG9idGFpbnMgYSBjb2xsZWN0aW9uIG9mIHRoZSBzdGF0aXN0aWNzLiBXZSB3aWxsIHRoZW4gdXNlIHRoZSBjb2xsZWN0aW9uIHRvIGFwcHJveGltYXRlIHByb2JhYmlsaXRpZXMgb3IgZXhwZWN0YXRpb25zIHRoYXQgYW5zd2VyIHRoZSBxdWVzdGlvbnMuDQoNCkxldCdzIGZpcnN0IGNvbnNpZGVyIFBldGVyJ3MgZm9ydHVuZSBgRmAgYXQgdGhlIGVuZCBvZiB0aGUgZ2FtZSAocXVlc3Rpb24gMykuIFdlIHdyaXRlIGEgZnVuY3Rpb24gYHBldGVyX3BhdWxgIHRoYXQgc2ltdWxhdGVzIHRoZSBmb3J0dW5lcyBmb3IgdGhlIDUwIHRvc3NlcyBhbmQgY29tcHV0ZXMgYEZgIHRoYXQgaXMgZXF1YWwgdG8gdGhlIHN1bSBvZiB0aGUgaW5kaXZpZHVhbCBmb3J0dW5lcy4gVG8gbWFrZSB0aGlzIGZ1bmN0aW9uIG1vcmUgZ2VuZXJhbCwgd2UgZGVmaW5lIGBuYCB0byBiZSB0aGUgbnVtYmVyIG9mIHRvc3NlcyBhbmQgbGV0IHRoZSBkZWZhdWx0IHZhbHVlIG9mIGBuYCBiZSBgNTBgLg0KDQpgYGB7cn0NCiMgV3JpdGUgYSBwZXRlcl9wYXVsIGZ1bmN0aW9uIHRoYXQgc2ltdWxhdGVzIHRoZSBmb3J0dW5lcyBmb3IgNTAgdG9zc2VzIGFuZCBjYWxjdWxhdGUgRiwgDQojIHRoYXQgaXMgdGhlIHN1bSBvZiB0aGUgaW5kaXZpZHVhbCBmb3J0dW5lcw0Kc2V0LnNlZWQoMjAyMCkNCnBldGVyX3BhdWw9ZnVuY3Rpb24obj01MCl7DQogIGZsaXA9RmxpcENvaW4obikNCiAgc3VtKGZsaXApDQp9DQpGID0gcGV0ZXJfcGF1bCgpDQpGDQpgYGANCg0KKipEZXNpcmVkIFJlc3VsdCoqDQpgYGANCiBbMV0gMjINCmBgYA0KDQoNCkluIHRoaXMgZ2FtZSwgUGV0ZXIgZmluaXNoZWQgd2l0aCBhIGZvcnR1bmUgb2YgMjLigqwuIFRvIHJlcGVhdCB0aGlzIGZvciAxMDAwIGdhbWVzLCB3ZSB1c2UgdGhlIHJlcGxpY2F0ZSBmdW5jdGlvbiB3aXRoIGFyZ3VtZW50cyBgMTAwMGAsIHRoZSBudW1iZXIgb2YgZ2FtZXMsIGFuZCB0aGUgbmFtZSBvZiB0aGUgZnVuY3Rpb24gYHBldGVyX3BhdWwoKWAgdG8gcmVwZWF0LiBUaGUgb3V0cHV0IG9mIHJlcGxpY2F0ZSBpcyB0aGUgdmVjdG9yIG9mIGZvcnR1bmVzIGZvciB0aGUgMTAwMCBnYW1lcyB0aGF0IHdlIGFzc2lnbiB0byB0aGUgdmFyaWFibGUgYEZgLiBQcmludCB0aGUgaGVhZGVyIG9mIHZhcmlhYmxlIGBGYCB0byBzZWUgdGhlIHJlc3VsdC4NCg0KYGBge3J9DQojIFVzZSB0aGUgcmVwbGljYXRlKCkgZnVuY3Rpb24gdG8gcmVwZWF0IHRoZSBleHBlcmltZW50IDEwMDAgdGltZXMNCnNldC5zZWVkKDIwMjApDQpGPXJlcGxpY2F0ZSgxMDAwLCBwZXRlcl9wYXVsKCkpDQpGWzE6Nl0NCmBgYA0KDQoqKkRlc2lyZWQgUmVzdWx0KioNCmBgYA0KWzFdIDIyIC0yICAyIC04IC0yIC00DQpgYGANCg0KIyMgU3VtbWFyaXppbmcgdGhlIE1vbnRlIENhcmxvIFJlc3VsdHMNCg0KU2luY2UgUGV0ZXIncyBmb3J0dW5lIGlzIGludGVnZXItdmFsdWVkLCBhIGNvbnZlbmllbnQgd2F5IG9mIHN1bW1hcml6aW5nIHRoZSBjb2xsZWN0aW9uIG9mIHZhbHVlcyBvZiBgRmAgdXNpbmcgYHRhYmxlKClgLg0KDQpgYGB7cn0NCiMgVXNlIHRoZSBmdW5jdGlvbiB0YWJsZSgpIHRvIHN1bWFyaXplIHRoZSB2YWx1ZXMgb2YgRg0KdGFibGUoRikNCmBgYA0KDQoqKkRlc2lyZWQgUmVzdWx0KioNCmBgYA0KRg0KLTIyIC0yMCAtMTggLTE2IC0xNCAtMTIgLTEwICAtOCAgLTYgIC00ICAtMiAgIDAgICAyICAgNCAgIDYgICA4ICAxMCAgMTIgIDE0ICAxNiAgMTggIDIwICAyMiANCiAgNCAgIDMgICA1ICAxNSAgIDggIDMxICAzMSAgNjkgIDcyIDExMSAxMDAgMTIwICA5NSAxMDkgIDc5ICA2MSAgMzYgIDIwICAxNCAgIDUgICA1ICAgNiAgIDEgDQpgYGANCg0KV2UgY2FuIGRpc3BsYXkgdGhlIGZyZXF1ZW5jaWVzIG9mIGBGYCB1c2luZyBgcGxvdCgpYCBhcHBsaWVkIHRvIHRoZSB0YWJsZSBvdXRwdXQsIGFuZCBzZWUgd2hhdCB3aWxsIGJlIHRoZSB2YWx1ZSBvZiBQZXRlcidzIGJlc3QgZm9ydHVuZSAocXVlc3Rpb24gMykNCg0KYGBge3J9DQojIFBsb3QNCnBsb3QodGFibGUoRikpDQoNCmBgYA0KDQoqKkRlc2lyZWQgUmVzdWx0KioNCkJlbG93IHlvdSBjYW4gc2VlIHRoZSBwbG90IHlvdSBoYXZlIHRvIG9idGFpbjoNCjxwIGFsaWduPSJjZW50ZXIiPg0KICA8aW1nIHNyYz0iRmlndXJlcy9wbG90Mi5wbmciIHdpZHRoPSI1MDAiPg0KPC9wPiANCg0KV2hhdCBpcyBQZXRlcidzIGNoYW5jZSBvZiBicmVha2luZyBldmVuPyAocXVlc3Rpb24gMSkgSXQgaXMgdGhlIHJhdGlvIChhcHByb3hpbWF0ZWQpIG9mIFBldGVyIGZpbmlzaGluZyB3aXRoIDAgb3V0IG9mIHRoZSAxMDAwDQoNCkJ1dCB0aGF0IGFuc3dlciBjYW4gYWxzbyBiZSBkZXRlcm1pbmVkIGV4YWN0bHkuIEhlIGJyZWFrcyBldmVuIGlmIHRoZXJlIGFyZSBleGFjdGx5IGBuLzJgIGhlYWRzIGluIGEgYmlub21pYWwgZXhwZXJpbWVudCBvZiBgbmAgdHJpYWxzIHdpdGggYSBwcm9iYWJpbGl0eSBvZiBzdWNjZXNzIGVxdWFsIHRvIGAwLjUwYC4gU28gaWYgYG4gPSA1MGAgd2UgY2FuIGFwcGx5IHRoZSBgZGJpbm9tYCBmdW5jdGlvbiB0byBvYnRhaW4gdGhlIGV4YWN0IHZhbHVlOg0KDQpgYGB7cn0NCiMgQ29tcHV0ZSBQKFg9MjUpIGZvciBYIEJpbm9taWFsKG49NTAscD0wLjUpDQo/ZGJpbm9tDQpkYmlub20oMjUsIDUwLCAwLjUpDQpwcmludCgxMjAvMTAwMCkNCmBgYA0KDQpOb3RlIGhvdyBjbG9zZSB0aGlzIGV4YWN0IG51bWJlciBpcyB0byBvdXIgYXBwcm94aW1hdGVkIGFuc3dlciBmcm9tIHRoZSBNb250ZSBDYXJsbyBzaW11bGF0aW9uLCB0aGF0IHNob3VsZCBiZSBgMTIwLzEwMDAgPWAgYHIgMTIwLzEwMDBgDQoNCiMjIE1vZGlmeWluZyB0aGUgRXhwZXJpbWVudCB0byBMZWFybiBBYm91dA0KIyMjIE5ldyBTdGF0aXN0aWNzDQoNCldlIGNhbiBhZGQgYWRkaXRpb25hbCBsaW5lcyBvZiBjb2RlIHRvIG91ciBmdW5jdGlvbiBgcGV0ZXJfcGF1bGAgdG8gY29tcHV0ZSBzZXZlcmFsIHN0YXRpc3RpY3Mgb2YgaW50ZXJlc3QgaW4gb3VyIGV4cGVyaW1lbnQuIFRvIGFuc3dlciBvdXIgcXVlc3Rpb25zLCB3ZSBmb2N1cyBvbiB0aGUgZmluYWwgZm9ydHVuZSBgRmAsIHRoZSBudW1iZXIgb2YgdGltZXMgUGV0ZXIgaXMgaW4gdGhlIGxlYWQgYExgLCBhbmQgdGhlIG1heGltdW0gY3VtdWxhdGl2ZSB3aW5uaW5nIGBNYC4gDQoNCkluIHRoZSBmdW5jdGlvbiwgd2UgZGVmaW5lIHRoZSB2ZWN0b3Igb2YgY3VtdWxhdGl2ZSB3aW5uaW5ncyBgY3VtX3dpbmAuIEhlcmUgdGhlIG91dHB1dCBvZiB0aGUgZnVuY3Rpb24gaXMgYSB2ZWN0b3IgY29uc2lzdGluZyBvZiB0aGUgdmFsdWVzIG9mIGBGYCwgYExgLCBhbmQgYE1gLiANCg0KQnkgbmFtaW5nIHRoZSBjb21wb25lbnRzICh1c2luZywgZm9yIGV4YW1wbGUsIEY9c3VtKHdpbikpLCB3ZSBnZXQgbW9yZSBhdHRyYWN0aXZlIG91dHB1dC4NCg0KYGBge3J9DQojIENyZWF0ZSBhIGZ1bmN0aW9uIHRvIG9idGFpbiB0aGUgZmluYWwgZm9ydHVuZSAoRiksIHRoZSBudW1iZXIgb2YgdGltZXMgUGV0ZXIgaXMgaW4gdGhlIGxlYWQgKEwpIGFuZCANCiMgdGhlIG1heGltdW0gY3VtdWxhdGl2ZSB3aW5uaW5nIChXKQ0Kc2V0LnNlZWQoMjAyMCkNCnBldGVyX3BhdWw9ZnVuY3Rpb24obj01MCl7DQogIGZsaXA9RmxpcENvaW4obikNCiAgY3VtX3dpbj1jdW1zdW0oZmxpcCkNCiAgYyhGID1zdW0oZmxpcCksIEw9c3VtKGN1bV93aW4+MCksICBNPW1heChjdW1fd2luKSkgICAgDQp9DQoNCmBgYA0KDQpXZSBzaW11bGF0ZSB0aGUgZ2FtZSBvbmNlDQoNCmBgYHtyfQ0KcGV0ZXJfcGF1bCgpDQpgYGANCg0KKipEZXNpcmVkIFJlc3VsdCoqDQpgYGANCiBGICBMICBNIA0KMjIgNTAgMjMgDQpgYGANCg0KSW4gdGhpcyBnYW1lICh3aXRoIHNlZWQgPSAyMDIwKSwgUGV0ZXIncyBmaW5hbCBmb3J0dW5lIHdhcyAyMuKCrCwgaGUgd2FzIGluIHRoZSBsZWFkIGZvciA1MCBwbGF5cywgYW5kIGhpcyBtYXhpbXVtIHRvdGFsIHdpbm5pbmcgZHVyaW5nIHRoZSBnYW1lIHdhcyAyM+KCrC4gVG8gcmVwZWF0IHRoaXMgZ2FtZSAxMDAwIHRpbWVzLCB3ZSBhZ2FpbiB1c2UgcmVwbGljYXRlIGFuZCBzdG9yZSBvdXRwdXQgaW4gdmFyaWFibGUgYFNgLg0KDQpTaW5jZSB0aGUgb3V0cHV0IG9mIGBwZXRlcl9wYXVsYCBpcyBhIHZlY3RvciwgYFNgIHdpbGwgYmUgYSBtYXRyaXggb2YgMyByb3dzIGFuZCAxMDAwIGNvbHVtbnMsIHdoZXJlIHRoZSByb3dzIGNvcnJlc3BvbmQgdG8gdGhlIHNpbXVsYXRlZCBkcmF3cyBvZiBgRmAsIGBMYCwgYW5kIGBNYC4gV2UgY2FuIHZlcmlmeSB0aGUgZGltZW5zaW9uIG9mIHRoZSBtYXRyaXggb2YgYFNgIHVzaW5nIHRoZSBgZGltKClgIGZ1bmN0aW9uLg0KDQpgYGB7cn0NCiMgU2ltdWxhdGUgMTAwMCB0aW1lcw0Kc2V0LnNlZWQoMjAyMCkNCnBldGVyX3BhdWw9ZnVuY3Rpb24obj01MCl7DQogIGZsaXA9RmxpcENvaW4obikNCiAgY3VtX3dpbj1jdW1zdW0oZmxpcCkNCiAgYyhGID1zdW0oZmxpcCksIEw9c3VtKGN1bV93aW4+MCksICBNPW1heChjdW1fd2luKSkgICAgDQp9DQpTID0gcmVwbGljYXRlKDEwMDAscGV0ZXJfcGF1bCgpKQ0KZGltKFMpDQpgYGANCg0KKipEZXNpcmVkIFJlc3VsdCoqDQpgYGANClsxXSAgICAzIDEwMDANCmBgYA0KDQpIb3cgbWFueSB0aW1lcyBQZXRlciBpbiB0aGUgbGVhZD8gVGhlIGxpa2VseSBhbnN3ZXIgdG8gdGhpcyBxdWVzdGlvbiAod2UgZG8gbm90IGtub3cgZm9yIHN1cmUpIGlzIGluIHRoZSBgTGAgcm93LiBDcmVhdGUgdGhlIGB0aW1lc19pbl9sZWFkYCB2YXJpYWJsZSB3aXRoIGFsbCB0aGUgdmFsdWVzIGFuZCBwcmludCB0aGUgaGVhZGVyDQoNCmBgYHtyfQ0KIyBIb3cgbWFueSB0aW1lcyBpcyBQZXRlciBpbiBsZWFkPw0KdGltZXNfaW5fbGVhZCA9IFNbIkwiLF0NCnRpbWVzX2luX2xlYWRbMTo2XQ0KYGBgDQoNCioqRGVzaXJlZCBSZXN1bHQqKg0KYGBgDQpbMV0gNTAgIDQgNDcgMTcgMTcgIDkNCmBgYA0KDQpXZSB0YWJ1bGF0ZSB0aGUgc2ltdWxhdGVkIHZhbHVlcyB1c2luZyB0aGUgYHRhYmxlYCBmdW5jdGlvbiwgYW5kIHRoZSBgcHJvcC50YWJsZWAgZnVuY3Rpb24gd2lsbCBmaW5kIHRoZSBjb3JyZXNwb25kaW5nIHJlbGF0aXZlIGZyZXF1ZW5jaWVzLiBXZSBwbG90IHRoZSByZXN1bHQgaW4gYSBsaW5lIGdyYXBoOg0KDQpgYGB7cn0NCiMgcGxvdA0KDQpwbG90KHByb3AudGFibGUodGFibGUodGltZXNfaW5fbGVhZCkpKQ0KYGBgDQoNCioqRGVzaXJlZCBSZXN1bHQqKg0KQmVsb3cgeW91IGNhbiBzZWUgdGhlIHBsb3QgeW91IGhhdmUgdG8gb2J0YWluOg0KPHAgYWxpZ249ImNlbnRlciI+DQogIDxpbWcgc3JjPSJGaWd1cmVzL3Bsb3QzLnBuZyIgd2lkdGg9IjUwMCI+DQo8L3A+IA0KDQpUaGUgcGF0dGVybiBvZiB0aGlzIGdyYXBoIGZvciBgTGAgaXNuJ3Qgd2hhdCBtb3N0IHBlb3BsZSBleHBlY3QuIFNpbmNlIGl0IGlzIGEgZmFpciBnYW1lLCBvbmUgbWlnaHQgdGhpbmsgaXQgd291bGQgYmUgbW9zdCBsaWtlbHkgZm9yIFBldGVyIHRvIGJlIGFoZWFkIGluIDI1IG91dCBvZiA1MCB0b3NzZXMuIA0KDQpJbnN0ZWFkLCB0aGUgbW9zdCBsaWtlbHkgdmFsdWVzIGFyZSB0aGUgZXh0cmVtZSB2YWx1ZXMgYEwgPSAwLDEsNTBgLCBhbmQgdGhlIHJlbWFpbmluZyB2YWx1ZXMgb2YgYExgIGFwcGVhciBlcXVhbGx5IGxpa2VseS4gU28gYWN0dWFsbHkgaXQgaXMgcmVsYXRpdmVseSBjb21tb24gZm9yIFBldGVyIHRvIGFsd2F5cyBiZSBsb3Npbmcgb3IgYWx3YXlzIHdpbm5pbmcgaW4gdGhpcyBnYW1lLg0KDQpMYXN0LCBsZXQncyBjb25zaWRlciB0aGUgZGlzdHJpYnV0aW9uIG9mIGBNYCwgUGV0ZXIncyBtYXhpbXVtIHdpbm5pbmcgZHVyaW5nIHRoZSA1MCBwbGF5cy4gV2Ugc3RvcmUgdGhlIDEwMDAgc2ltdWxhdGVkIHZhbHVlcyBvZiBgTWAgaW4gdGhlIHZhcmlhYmxlIGBtYXhpbXVtX2xlYWRgLCB0YWJsdWxhdGUgdGhlIHZhbHVlcyB1c2luZyBgdGFibGUoKWAsIGFuZCB0aGVuIHBsb3QgdGhlbQ0KDQpgYGB7cn0NCiMgUGxvdCB0aGUgTWF4aW11bSB3aW5pbmdzIGluIGFsbCB0aGUgZXhwZXJpbWVudHMNCm1heGltdW1fbGVhZCA9IFNbIk0iLF0NCiNtYXhpbXVtX2xlYWRbMTo2XQ0KcGxvdChwcm9wLnRhYmxlKHRhYmxlKG1heGltdW1fbGVhZCkpKQ0KYGBgDQoNCioqRGVzaXJlZCBSZXN1bHQqKg0KQmVsb3cgeW91IGNhbiBzZWUgdGhlIHBsb3QgeW91IGhhdmUgdG8gb2J0YWluOg0KPHAgYWxpZ249ImNlbnRlciI+DQogIDxpbWcgc3JjPSJGaWd1cmVzL3Bsb3Q0LnBuZyIgd2lkdGg9IjUwMCI+DQo8L3A+IA0KDQpGcm9tIHRoZSBwbG90LCB3ZSBjYW4gc2VlIHRoYXQgaXQgaXMgbW9zdCBsaWtlbHkgZm9yIFBldGVyIHRvIGhhdmUgbWF4aW11bSB3aW5uaW5ncyBvZiAxIG9yIDIsIGJ1dCB2YWx1ZXMgYmV0d2VlbiAzIGFuZCA2IGFyZSBhbGwgcmVsYXRpdmVseSBsaWtlbHkuDQoNClRvIGNvbXB1dGUgdGhlIGFwcHJveGltYXRlIHByb2JhYmlsaXR5IHRoYXQgUGV0ZXIgd2lsbCBoYXZlIGEgbWF4aW11bSB3aW5uaW5nIG9mIDEwIG9yIG1vcmUsIHdlIGZpbmQgdGhlIG51bWJlciBvZiB2YWx1ZXMgb2YgYG1heGltdW1fbGVhZGAgdGhhdCBhcmUgMTAgb3IgZ3JlYXRlciBhbmQgZGl2aWRlIHRoaXMgc3VtIGJ5IHRoZSBudW1iZXIgb2Ygc2ltdWxhdGlvbiBpdGVyYXRpb25zIDEwMDA6DQoNCmBgYHtyfQ0KIyBXaGF0IGlzIHRoZSBudW1iZXIgb2YgdGltZXMgUGV0ZXIgaGF2ZSBhIG1heGltdW0gbGVhZCBncmVhdGVyIHRoYW4gMTA/DQpzdW0obWF4aW11bV9sZWFkID49IDEwKS8xMDAwDQpgYGANCg0KU2luY2UgdGhpcyBwcm9iYWJpbGl0eSBpcyBvbmx5IGFib3V0IDE2JSwgaXQgaXMgcmVsYXRpdmVseSByYXJlIGZvciBQZXRlciB0byBoYXZlIGEgbWF4aW11bSB3aW5uaW5nIG9mIDEwIG9yIG1vcmUuDQoNCg0KIyBBcHBseSB3aGF0IHlvdSBoYXZlIGxlYXJuZWQNCg0KSWYgeW91IGhhdmUgbWFuYWdlZCB0byBnZXQgdGhpcyBmYXIgeW91IGhhdmUgc2VjdXJlZCA4MCUgb2YgdGhlIGdyYWRlLg0KDQpJbiB0aGlzIGxhc3Qgc2VjdGlvbiBvZiB0aGUgbGFiIHlvdSB3aWxsIGhhdmUgdG8gYmUgY3JlYXRpdmUgYW5kIGdlbmVyYXRlIG5ldyBxdWVzdGlvbnMgYWJvdXQgdGhlIGNvaW4gZ2FtZSB0aGF0IHlvdSBjYW4gbGF0ZXIgc29sdmUgdXNpbmcgUi1zaW11bGF0aW9uIGV4cGVyaW1lbnRzLg0KDQpHZW5lcmF0ZSBhdCBsZWFzdCBvbmUgbmV3IHF1ZXN0aW9uIGFuZCBzb2x2ZSBpdC4gDQoNCiMgUXVlc3Rpb246DQpXaGF0IGFyZSB0aGUgcHJvYmFiaWxpdGllcyBvZiBQZXRlciB0byAqKndpbiBjb25zZWN1dGl2ZWx5KiogZm9yIDUwIHRvc3NlcyA/IA0KDQpUbyBkZW1vbnN0cmF0ZSB0aGUgaWRlYSwgd2Ugd2lsbCB0b3NzIHRoZSBjb2luIG9ubHkgZm9yIDEwIHRpbWVzIGluIG9ubHkgMSBnYW1lDQpgYGB7cn0NCnNldC5zZWVkKDIwMjApDQpmbGlwPUZsaXBDb2luKDEwKQ0KZmxpcA0KDQpgYGANCg0KDQpXZSBjYW4gY291bnQgdGhlIGNvbnNlY3V0aXZlIGJ5IHVzaW5nIGZ1bmN0aW9uIHJsZS4gSGVyZSBsZW5ndGhzIHJlcHJlc2VudCBudW1iZXIgb2Ygc2VxdWVuY2Ugb2YgYm90aCAtMSBhbmQgMS4gDQpGb3IgZXhhbXBsZToNCg0KLSAxKSBJdCBzaG93cyBoZXJlIHRoYXQgb25seSAtMSBoYXMgc2VxdWVuY2Ugb2YgMQ0KLSAyKSAtMSBoYXMgMSBmcmVxdWVuY3kgb2YgaGF2aW5nIDEgY29uc2VjdXRpdmUgKGxvc3QpDQotIDMpIC0xIGhhcyAxIGZyZXF1ZW5jeSBvZiBoYXZpbmcgMiBjb25zZWN1dGl2ZSAobG9zdCkNCi0gNCkgMSBoYXMgMiBmcmVxdWVuY2llcyBvZiBoYXZpbmcgMiBjb25zZWN1dGl2ZSh3aW4pDQotIDUpIDEgaGFzIDEgZnJlcXVlbmN5IG9mIGhhdmluZyAzIGNvbnNlY3V0aXZlKHdpbikNCmBgYHtyfQ0KZmxpcF9zdDwtcmxlKGZsaXApDQp0YWJsZShmbGlwX3N0KQ0KYGBgDQpOb3cgd2Ugd2lsbCBjYWxjdWxhdGUgZmlyc3QgdGhlICoqY29uc2VjdXRpdmUgd2lubmluZyoqIGFuZCBzdW0gdGhlIHJlc3QgYW5kIGFzc2lnbiB6ZXJvIHZhbHVlIHRvIHNob3cgZXZlbnRzIHdpdGhvdXQgY29uc2VjdXRpdmUgd2lubmluZw0KYGBge3J9DQp3aW4yY29uID0gZmxpcF9zdCRsZW5ndGhzW3doaWNoKGZsaXBfc3QkdmFsdWVzPT0xKV0NCm90aGVyX3dpbjJjb24gPSBmbGlwX3N0JGxlbmd0aHNbd2hpY2goZmxpcF9zdCR2YWx1ZXM9PSAtIDEpXQ0Kb3RoZXJfd2luMmNvbiA9IHN1bShvdGhlcl93aW4yY29uKQ0Kb3RoZXJfd2luMmNvbg0Kd2luMmNvbg0KZWxfd2luMW9ybG9zdCA9IDEwIC0gc3VtKHdpbjJjb24pDQplbF93aW4xb3Jsb3N0DQpgYGANCldlIHdpbGwgbmVlZCB0byBjcmVhdGUgZW50cmllcyBmb3IgZXZlbnQgb29mIG5vIGNvbnNlY3V0aXZlIHdpbiBvciBsb3NzZXMgYW5kIGFzc2lnbiB0aGlzIHZhcmlhYmxlICoqd2luMW9ybG9zdCoqLiBXZSB1c2UgZnVuY3Rpb24gcmVwIHRvIGFkZCB6ZXJvIGZvciB0aG9zZSBldmVudC4NCldlIGNvbWJpbmUgYm90aCAqKndpbjJtb3JlKiogYW5kICoqd2luMW9ybG9zdCoqIGFuZCBhc3NpZ24gbmV3IHRhYmxlICoqZmluYWxfdGFiKioNCmBgYHtyfQ0Kd2luMW9ybG9zdDwtIHJlcCgwLGVsX3dpbjFvcmxvc3QpDQpmaW5hbF90YWIgPSBhcHBlbmQod2luMmNvbix3aW4xb3Jsb3N0KQ0KZmluYWxfdGFiDQpgYGANCk5vdyBsZXQgdXMgY2FsY3VsYXRlIHRoZSBmcmVxdWVuY2llcyBvZiB0aGUgY29uc2VjdXRpdmUgd2lubmluZyBhbmQgaGF2ZSB0aGUgcHJvYmFiaWxpdHkgdGFibGUNCmBgYHtyfQ0KdGFibGUgKGZpbmFsX3RhYikNCnByb3AudGFibGUodGFibGUgKGZpbmFsX3RhYikpDQpgYGANCkJ5IHRvc3NpbmcgdGhlIGNvaW4gYnkgMTAgdGltZXMsIHdlIGtub3cgdGhhdCB3ZSBnb2luZyB0byBoYXZlIDMgY29uc2VjdXRpdmUgd2luIG9ubHkgb25jZSwgMiBjb25zZWN1dGl2ZSB3aW4gdHdpY2UgYW5kIG90aGVycyB3aGljaCBjb25zaXN0IG9mIG9ubHkgd2lubmluZyBvbmNlIG9yIGxvc3QgYnkgdGhyaWNlLiANCg0KTGV0IHVzIHB1dCB0aGlzIGluIGEgKipmdW5jdGlvbiBhbmQgcnVuIDUwIHRvc3NpbmcgY29pbnMgYW5kIHJlcGxpY2F0ZSB0aGlzIDEwMDAgdGltZXMgdG8gZ2V0IHRoZSBkaXN0cmlidXRpb24gcHJvYmFiaWxpdHkgb2YgY29uc2VjdXRpdmUgd2lubmluZy4NCmBgYHtyfQ0KIyBXcml0ZSB5b3VyIGNvZGUgaGVyZQ0Kc2V0LnNlZWQoMjAyMCkNCnBldGVyX3BhdWxfc3RyZWFrIDwtIGZ1bmN0aW9uKG49NTApew0KICANCiAgZmxpcD1GbGlwQ29pbihuKQ0KICBmbGlwX3N0PC1ybGUoZmxpcCkNCiAgd2luMmNvbiA9IGZsaXBfc3QkbGVuZ3Roc1t3aGljaChmbGlwX3N0JHZhbHVlcz09MSldDQogIG90aGVyX3dpbjJjb24gPSBmbGlwX3N0JGxlbmd0aHNbd2hpY2goZmxpcF9zdCR2YWx1ZXM9PSAtIDEpXQ0KICBvdGhlcl93aW4yY29uID0gc3VtKG90aGVyX3dpbjJjb24pDQogICMgY2FsY3VsYXRlIGV2ZW50IG9mIG9ubHkgbm8gY29uc2VjdXRpdmUgb3IgbG9zdA0KICBlbF93aW4xb3Jsb3N0ID0gbiAtIHN1bSh3aW4yY29uKQ0KICAjI2Fzc2lnbmVkIHplcm8gZm9yIG90aGVyIGNvbmRpdGlvbiAobm8gY29uc2VjdXRpdmUgd2luIG9yIGxvc3QpDQogIHdpbjFvcmxvc3Q8LSByZXAoMCxlbF93aW4xb3Jsb3N0KQ0KICBmaW5hbF90YWIgPSBhcHBlbmQod2luMmNvbix3aW4xb3Jsb3N0KQ0KICANCiAgDQp9DQojIFdlIHVzZSBNb250ZSBDYXJsbyBhcHByb2FjaCBieSBydW5uaW5nIHRoZSBmdW5jdGlvbiAxMDAwIHRpbWVzIGZvciA1MCANCldpbm5pbmcyTW9yZSA9IHJlcGxpY2F0ZSgxMDAwLHBldGVyX3BhdWxfc3RyZWFrKDUwKSkNCiMgd2UgZG8gdW5saXN0IHRvIGhhbmRsZSBkaWZmZXJlbnQgc2l6ZSBvZiBsaXN0IGZyb20gdGhlIGNvbnNlY3V0aXZlIHJlc3VsdA0KdGFibGUodW5saXN0KFdpbm5pbmcyTW9yZSkpDQpgYGANClRhYmxlIGFib3ZlIHNob3dzIHRoZSBmcmVxdWVuY3kgb2YgdGhlIHdpbm5pbmcgaW4gc2VxdWVuY2UgZnJvbSAxIHRvIDEyLCBtZWFud2hpbGUsIHplcm8gcmVwcmVzZW50IG5vIGNvbnNlY3V0aXZlIHdpbiBvciBsb3N0LiBXZSBjYW4gc2VlIHRoYXQgdGhlIG51bWJlciBvZiBtYXhpbXVtIDEyIGNvbnNlY3V0aXZlIHdpbiB3aGljaCBoYXBwZW4gb25seSBvbmNlLiAgQW5kIHdlIGNhbiBzZWUgdGhhdCB0aGUgbnVtYmVyIG9mIGNvbnNlY3V0aXZlIHdpbm5pbmcgaXMgZGVjcmVhc2luZw0KYGBge3J9DQpyb3VuZChwcm9wLnRhYmxlKHRhYmxlKHVubGlzdChXaW5uaW5nMk1vcmUpKSksMykNCmBgYA0KDQpUYWJsZSBoZXJlIHNob3dzIHRoZSBwcm9iYWJpbGl0eSBvZiB0aGUgY29uc2VjdXRpdmUgd2lubmluZy4gV2UgY2FuIHNlZSB0aGF0IHRoZSBwcm9iYWJpbGl0eSBvZiB3aW5uaW5nIDYgb3IgbW9yZSBjb25zZWN1dGl2ZWx5IGlzIHZlcnkgbG93IChiZWxvdyAwLjAwKQ0KYGBge3J9DQpwbG90KHByb3AudGFibGUodGFibGUodW5saXN0KFdpbm5pbmcyTW9yZSkpKSwgbWFpbiA9IHBhc3RlKCJQcm9iYWJpbGl0eSBvZiBXaW5uaW5nIGluIFNlcXVlbmNlIikseGxhYj0iRnJlcXVlbmNpZXMiLA0KeWxhYj0icHJvYmFiaWxpdHkiKQ0KDQpgYGANCkhlcmUgaXMgdGhlIGNoYXJ0DQo=