BASICS

Type ‘q()’ to quit R.

Asking for help:

rr # When you know the function name, use one question mark. ?tail

When you sort of know, use two question marks.

??average ??tail

Install igraph, haven, and tidyverse:

install.packages('tidyverse')
trying URL 'https://cran.rstudio.com/bin/macosx/el-capitan/contrib/3.6/tidyverse_1.2.1.tgz'
Content type 'application/x-gzip' length 89217 bytes (87 KB)
==================================================
downloaded 87 KB

The downloaded binary packages are in
    /var/folders/zm/zhc689mj20sf8gz6nfjt09r40000gn/T//Rtmpy32DoF/downloaded_packages
install.packages('igraph')
Error in install.packages : Updating loaded packages
install.packages("haven")
trying URL 'https://cran.rstudio.com/bin/macosx/el-capitan/contrib/3.6/haven_2.2.0.tgz'
Content type 'application/x-gzip' length 1077133 bytes (1.0 MB)
==================================================
downloaded 1.0 MB

The downloaded binary packages are in
    /var/folders/zm/zhc689mj20sf8gz6nfjt09r40000gn/T//Rtmpy32DoF/downloaded_packages

Set seed:

rr set.seed(09032019)

Load igraph , haven, and tidyverse OR attach data already loaded in R:

library(igraph)

Attaching package: ‘igraph’

The following objects are masked from ‘package:stats’:

    decompose, spectrum

The following object is masked from ‘package:base’:

    union
library(tidyverse)
Registered S3 method overwritten by 'dplyr':
  method           from
  print.rowwise_df     
── Attaching packages ───────────────────────────────────────────── tidyverse 1.2.1 ──
✔ ggplot2 3.2.1     ✔ purrr   0.3.2
✔ tibble  2.1.3     ✔ dplyr   0.8.3
✔ tidyr   1.0.0     ✔ stringr 1.4.0
✔ readr   1.3.1     ✔ forcats 0.4.0
── Conflicts ──────────────────────────────────────────────── tidyverse_conflicts() ──
✖ dplyr::as_data_frame() masks tibble::as_data_frame(), igraph::as_data_frame()
✖ purrr::compose()       masks igraph::compose()
✖ tidyr::crossing()      masks igraph::crossing()
✖ dplyr::filter()        masks stats::filter()
✖ dplyr::groups()        masks igraph::groups()
✖ dplyr::lag()           masks stats::lag()
✖ purrr::simplify()      masks igraph::simplify()
library(haven)

library(readxl) 

attach(mtcars)
The following object is masked from package:ggplot2:

    mpg

Read in data from a desktop:

data2 <- read.delim('~/Documents/UCLA/Data/PRIO_UrbanSocialDisorder/events.csv', sep=',')
cannot open file '/Users/Al/Documents/UCLA/Data/PRIO_UrbanSocialDisorder/events.csv': No such file or directoryError in file(file, "rt") : cannot open the connection

Read in data from a website:

# This step only needed to read excel files from the internet:
download.file("https://stats.idre.ucla.edu/stat/data/hsb2.xls", "~/Downloads/myfile.xls", mode="wb")
trying URL 'https://stats.idre.ucla.edu/stat/data/hsb2.xls'
Content type 'application/vnd.ms-excel' length 41984 bytes (41 KB)
==================================================
downloaded 41 KB

Use read_graph to load network data. Create a network from an edgelist. Load data at https://raw.githubusercontent.com/ZacharyST/Data/master/dolphins. Open that link first in your web browser so you know what the data look like. The data are undirected (two options):

data <- graph_from_edgelist(as.matrix(data), directed = FALSE)
Error in as.vector(x, mode) : 
  cannot coerce type 'closure' to vector of type 'any'

Get the adjacency matrix from the graph above. Tell the function not to return a sparse matrix.

rr amps2 <- as_adjacency_matrix(dataps2, sparse = FALSE)

Load the dolphin data again, this time telling R the network is directed.

rr dataps2d <- read_graph(://raw.githubusercontent.com/ZacharyST/Data/master/dolphins, directed = TRUE)

Load the SCAD data from my Github Data repository. The link is https://raw.githubusercontent.com/ZacharyST/Data/master/scad_latiname rica.

rr data <- read.csv(://raw.githubusercontent.com/ZacharyST/Data/master/scad_latinamerica)

INSPECTING A NETWORK (edges, nodes, components, size, degree)

How many nodes does the network have (two options)?

length(V(dataps2))
[1] 63

How many nodes does the network have (two options) using an adjacency matrix?

ncol(amps2)
[1] 63

How many edges does the network have (three options)?

sum(colSums(amps2))/2
[1] 159

Does the network have any components?

count_components(dataps2)
[1] 2

Save the results from above as a new object. Call the entry of that object that tells you have many components there are.

c$no
[1] 2

What is the size of the component(s)?

c$csize
[1]  1 62

What is the degree of each node? Use the object created when you read in the file.

degree(dataps2)
 [1]  0  6  8  4  3  1  4  6  5  6  7  5  1  1  8 12  7  6  9  7  4  9  6  1  3  6  3
[28]  3  5  5  9  5  1  3 10  5  1  7 11  8  2  8  5  6  7  4 11  2  6  1  2  7 10  4
[55]  2  7  2  2  9  1  5  1  3

What is the degree of each node? Use the adjacency matrix?

rowSums(amps2)
 [1]  0  6  8  4  3  1  4  6  5  6  7  5  1  1  8 12  7  6  9  7  4  9  6  1  3  6  3
[28]  3  5  5  9  5  1  3 10  5  1  7 11  8  2  8  5  6  7  4 11  2  6  1  2  7 10  4
[55]  2  7  2  2  9  1  5  1  3

What is the degree of the most social dolphin?

max(degree(dataps2))
[1] 12

Which dolphin is the most social?

V(dataps2)[degree(dataps2) == max(degree(dataps2))]
+ 1/63 vertex, from c1bd9b9:
[1] 16

Repeat the last two queries above, but save the result of the most social dolphin as an object.

V(dataps2)[degree(dataps2) == social]
+ 1/63 vertex, from c1bd9b9:
[1] 16

Load and inspect the KARATE data.

Make column names match the row numbers:

Now, let us count how many nodes there are, the number of components, and the degree of each node:

rr library(igraph) # To load igraph functions g <- graph(d) # Fails. Requires edgelist. g <- graph_from_adjacency_matrix(d) # Fails because d is a dataframe. g <- graph_from_adjacency_matrix(as.matrix(d), mode=) # Works. Do not forget to tell it the kind of network you have.

To just get the count of components:

count_components(g)
[1] 1

The most connections and its node or nodes (two options):

V(g)[degree(g) == maximum]
+ 1/34 vertex, named, from b64e902:
[1] 34

Loop, check work, correct:

# Using the apply function:  
# MARGIN=2 means columns.
apply(d, MARGIN=2, FUN=sum)  # You have to capitalize the argument names.
 1  2  3  4  5  6  7  8  9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 
16  9 10  6  3  4  4  4  5  2  3  1  2  5  2  2  2  2  2  3  2  2  2  5  3  3  2  4 
29 30 31 32 33 34 
 3  4  4  6 12 17 

Find the nodes in the karate network that have the minimum degree.

V(g)[which.min(degree(g))]
+ 1/34 vertex, named, from b64e902:
[1] 12

RANDOM NUMBER GENERATOR (igraph, adjacency matrix)

A binomial distribution is one that produces a series of numbers according to parameters you pass it.We can easily make it produces 1s and 0s and then populate an adjacency matrix with them. The last argument controls the ratio of 1s and 0s. So, half the output will be 1, half will be 0, on average.

rbinom(1,1,.5)
[1] 1
rbinom(1,1,.5)
[1] 1
rbinom(1,1,.5)
[1] 1
rbinom(1,1,.5)
[1] 1
rbinom(1,1,.5)
[1] 0

Let’s speed that up. Why does the below line produce more numbers? 10 replaces 1.

rbinom(10,1,.5)
 [1] 0 0 1 0 1 1 0 1 1 1

Let’s understand rbinom a little more. Second number is options.

rbinom(10,3,.5)
 [1] 2 1 2 0 1 1 2 1 0 3

We can now make a matrix of arbitrary size, so long as rbinom() creates a square amount of numbers.

temp
      [,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8] [,9]
 [1,]    1    0    0    0    0    0    0    1    1
 [2,]    0    1    0    1    1    1    1    0    1
 [3,]    0    0    1    0    0    0    1    1    1
 [4,]    0    0    0    0    0    0    1    1    1
 [5,]    1    0    0    1    1    1    1    1    0
 [6,]    0    0    1    1    0    0    0    1    0
 [7,]    1    1    1    1    0    0    1    0    1
 [8,]    1    1    0    0    0    0    1    1    1
 [9,]    0    0    0    1    1    0    0    1    0

Maybe this approach is a little more intuitive:

temp
      [,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8] [,9] [,10] [,11] [,12] [,13] [,14]
 [1,]    0    0    0    0    0    1    0    0    0     1     1     1     0     1
 [2,]    0    0    1    0    1    1    0    1    0     0     0     1     1     1
 [3,]    0    1    0    1    0    0    0    1    0     0     0     1     0     0
 [4,]    0    1    1    0    1    0    0    0    0     1     1     1     1     0
 [5,]    0    0    0    0    0    0    1    0    1     0     0     1     1     1
 [6,]    0    0    0    0    1    0    0    0    1     1     0     0     1     1
 [7,]    0    0    1    1    1    1    0    1    0     0     0     1     0     1
 [8,]    1    1    0    0    1    0    0    0    1     0     0     1     1     0
 [9,]    0    0    1    0    0    1    1    1    0     0     0     1     0     0
[10,]    1    0    1    1    1    1    0    0    1     0     1     1     1     0
[11,]    1    0    0    1    1    1    1    0    0     1     0     0     1     1
[12,]    1    0    0    0    0    1    1    1    0     1     0     0     0     1
[13,]    0    1    1    0    1    1    0    1    0     0     1     0     0     0
[14,]    0    1    0    0    0    1    0    0    0     1     0     0     1     0
[15,]    1    1    1    1    0    1    1    1    1     1     0     0     0     0
      [,15]
 [1,]     1
 [2,]     0
 [3,]     0
 [4,]     0
 [5,]     1
 [6,]     1
 [7,]     0
 [8,]     0
 [9,]     0
[10,]     1
[11,]     0
[12,]     1
[13,]     0
[14,]     1
[15,]     0

Convert to igraph object.

rr g_temp <- graph_from_adjacency_matrix(temp, mode=)

Note that this approach requires using a directed network.

(colSums(temp) + rowSums(temp)) == degree(g_temp)
 [1] TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE

You can also make an undirected graph from the adjacency matrix. igraph will handle the construction of edges by assuming any edge is symmetric.

degree(g_temp2) # Gives the same as the above for graph
 [1]  7  9  9  9 12 11 10  9  8 11  8 12  9 10 12

But calculation of other statistics will be tricky. It is better to just make the matrix symmetric right away, which requires some massaging. For the bottom left half of the matrix, make it the same as the top right.

temp2
      [,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8] [,9] [,10] [,11] [,12] [,13] [,14]
 [1,]    0    0    0    0    0    1    0    0    0     1     1     1     0     1
 [2,]    0    0    1    0    1    1    0    1    0     0     0     1     1     1
 [3,]    0    1    0    1    0    0    0    1    0     0     0     1     0     0
 [4,]    0    0    1    0    1    0    0    0    0     1     1     1     1     0
 [5,]    0    1    0    1    0    0    1    0    1     0     0     1     1     1
 [6,]    1    1    0    0    0    0    0    0    1     1     0     0     1     1
 [7,]    0    0    0    0    1    0    0    1    0     0     0     1     0     1
 [8,]    0    1    1    0    0    0    1    0    1     0     0     1     1     0
 [9,]    0    0    0    0    1    1    0    1    0     0     0     1     0     0
[10,]    1    0    0    1    0    1    0    0    0     0     1     1     1     0
[11,]    1    0    0    1    0    0    0    0    0     1     0     0     1     1
[12,]    1    1    1    1    1    0    1    1    1     1     0     0     0     1
[13,]    0    1    0    1    1    1    0    1    0     1     1     0     0     0
[14,]    1    1    0    0    1    1    1    0    0     0     1     1     0     0
[15,]    1    0    0    0    1    1    0    0    0     1     0     1     0     1
      [,15]
 [1,]     1
 [2,]     0
 [3,]     0
 [4,]     0
 [5,]     1
 [6,]     1
 [7,]     0
 [8,]     0
 [9,]     0
[10,]     1
[11,]     0
[12,]     1
[13,]     0
[14,]     1
[15,]     0

ADJACENCY MATRIX EXAMPLE

Create an adjacency matrix that has 25 rows and columns, where each cell has a .8 probability of containing a 1. Set the diagonal to zero.

probability <- .8
thisnumber <- 25
thismatrix <- matrix(rbinom(thisnumber^2, 1, probability), nrow=thisnumber)
diag(thismatrix) <- 0
thismatrix
      [,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8] [,9] [,10] [,11] [,12] [,13] [,14]
 [1,]    0    1    1    0    0    0    1    1    0     1     1     1     1     0
 [2,]    1    0    1    1    1    1    0    1    0     1     1     0     1     1
 [3,]    0    1    0    1    1    1    0    1    1     1     1     1     1     1
 [4,]    1    1    1    0    1    1    1    1    1     1     1     1     1     0
 [5,]    1    1    1    1    0    1    1    1    1     0     1     1     1     0
 [6,]    1    1    1    1    1    0    1    0    1     1     1     0     1     1
 [7,]    1    1    0    0    1    0    0    1    1     1     1     1     1     1
 [8,]    0    0    1    0    1    1    1    0    1     0     1     0     0     1
 [9,]    1    1    1    1    1    1    1    0    0     1     1     1     1     1
[10,]    0    1    0    0    1    1    1    1    1     0     1     0     1     1
[11,]    1    1    1    1    1    1    1    1    1     1     0     1     1     1
[12,]    1    1    1    1    1    1    1    0    1     1     1     0     1     1
[13,]    1    1    1    1    1    1    1    1    1     1     1     0     0     1
[14,]    1    1    1    1    1    1    1    1    1     1     0     1     1     0
[15,]    1    1    1    1    1    1    1    1    1     1     1     1     0     1
[16,]    1    1    1    1    1    1    0    1    1     0     1     0     1     1
[17,]    1    1    1    1    0    1    0    1    0     1     0     1     0     0
[18,]    0    1    0    0    1    1    1    1    0     1     0     1     1     0
[19,]    1    1    1    1    1    1    1    1    1     1     0     1     1     1
[20,]    1    0    1    1    1    0    0    0    0     1     1     1     1     1
[21,]    1    1    1    1    1    1    1    1    1     0     1     0     0     1
[22,]    0    1    1    1    1    1    1    0    1     0     1     1     1     1
[23,]    1    1    1    1    1    1    1    1    1     1     1     1     0     1
[24,]    1    1    1    1    1    1    1    1    1     1     1     0     1     1
[25,]    1    0    1    0    0    1    1    1    1     1     1     1     1     1
      [,15] [,16] [,17] [,18] [,19] [,20] [,21] [,22] [,23] [,24] [,25]
 [1,]     0     1     1     0     1     1     1     1     1     1     0
 [2,]     1     1     1     1     1     0     0     1     1     1     1
 [3,]     1     0     1     1     1     1     0     0     0     1     0
 [4,]     1     1     0     0     1     1     1     1     1     1     0
 [5,]     0     1     1     0     1     1     1     1     0     0     1
 [6,]     1     0     0     1     1     1     1     1     0     1     0
 [7,]     1     1     0     1     0     1     1     1     1     0     1
 [8,]     0     1     1     0     1     1     1     1     1     1     1
 [9,]     1     0     1     0     1     0     1     1     1     1     1
[10,]     0     0     1     1     1     1     1     0     1     1     1
[11,]     1     1     1     0     1     1     1     0     0     1     0
[12,]     1     1     0     1     1     1     1     1     1     0     1
[13,]     1     1     1     1     1     1     1     1     1     0     1
[14,]     0     1     1     1     1     0     1     1     1     1     1
[15,]     0     1     0     1     1     1     1     1     1     1     1
[16,]     0     0     1     1     1     1     1     1     1     1     1
[17,]     1     1     0     1     0     1     1     0     1     1     1
[18,]     0     0     1     0     1     1     1     1     0     1     1
[19,]     1     0     1     1     0     1     1     0     1     1     0
[20,]     1     1     1     0     1     0     1     1     1     1     0
[21,]     1     0     0     0     1     1     0     1     0     1     1
[22,]     1     1     1     1     1     0     1     0     1     1     1
[23,]     1     0     0     1     1     1     1     1     0     1     1
[24,]     1     0     1     1     1     0     1     1     1     0     1
[25,]     1     1     1     0     1     1     1     0     1     1     0

Convert it to an undirected network.

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

Convert the matrix to an igraph graph object.

rr ig2 <- graph_from_adjacency_matrix(thismatrix, mode=‘undirected’)

What is the average degree of the network?

summary(degree(ig2))[4]
 Mean 
18.64 

What is the density of the network?

edge_density(ig2)  # same
[1] 0.7766667

What is the global transitivity of the network?

transitivity(ig2, type='globalundirected')
[1] 0.7654172

Use an adjacency matrix to create a fake network that has 30 individuals.

newmatrix
      [,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8] [,9] [,10] [,11] [,12] [,13] [,14]
 [1,]    0    1    0    0    1    0    0    0    0     1     1     0     1     0
 [2,]    1    1    0    0    0    1    0    0    0     0     1     1     0     0
 [3,]    0    0    0    0    1    1    0    0    1     0     0     0     1     1
 [4,]    0    0    0    0    1    0    1    0    1     0     0     0     1     0
 [5,]    1    1    0    1    1    1    0    1    0     0     0     0     1     0
 [6,]    1    1    0    0    0    0    1    0    1     0     0     1     0     0
 [7,]    1    1    0    1    0    0    0    1    0     0     0     0     0     1
 [8,]    0    0    0    0    0    1    1    0    0     1     0     0     1     1
 [9,]    1    0    0    1    0    1    1    1    0     0     1     1     0     1
[10,]    0    0    1    1    0    1    0    1    0     1     0     1     0     1
[11,]    1    0    0    1    1    0    0    1    0     0     0     1     1     1
[12,]    0    1    0    1    1    0    1    0    1     0     1     1     1     0
[13,]    1    0    0    0    0    0    1    0    0     1     1     0     1     1
[14,]    1    0    0    0    0    1    0    1    0     1     1     0     1     0
[15,]    0    1    0    0    0    0    0    1    1     1     1     0     0     0
[16,]    1    0    0    0    0    1    1    0    1     1     0     0     0     1
[17,]    1    0    1    1    1    0    0    0    0     1     0     0     0     1
[18,]    1    0    0    0    1    0    0    1    1     0     0     0     0     0
[19,]    1    1    0    0    1    1    1    1    0     1     1     1     1     0
[20,]    0    1    1    0    1    0    0    0    1     0     0     1     1     1
[21,]    0    0    0    1    1    1    1    0    1     1     0     0     1     0
[22,]    0    1    0    0    1    0    1    1    0     0     0     0     1     1
[23,]    0    1    0    1    0    1    0    1    0     1     0     0     1     0
[24,]    1    0    1    0    0    1    1    0    0     0     0     0     1     1
[25,]    0    1    0    0    1    1    1    1    1     0     1     1     1     1
[26,]    0    1    1    1    1    0    1    0    0     0     1     0     0     1
[27,]    0    1    0    0    0    1    0    1    1     0     1     0     0     0
[28,]    1    0    0    0    1    1    1    0    0     0     1     0     0     1
[29,]    0    1    1    1    0    1    1    1    1     0     1     0     0     0
[30,]    1    1    1    0    1    1    1    0    0     1     0     0     0     0
      [,15] [,16] [,17] [,18] [,19] [,20] [,21] [,22] [,23] [,24] [,25] [,26] [,27]
 [1,]     1     1     0     0     0     0     1     1     1     1     1     0     1
 [2,]     1     1     1     1     0     0     1     0     1     1     1     1     1
 [3,]     1     1     1     1     0     1     0     1     0     0     1     0     0
 [4,]     0     0     0     0     0     0     0     1     0     0     0     1     1
 [5,]     0     1     1     1     0     1     1     0     0     0     1     1     1
 [6,]     0     0     1     0     0     0     1     1     0     0     0     1     1
 [7,]     1     0     1     1     0     0     0     1     1     1     1     0     1
 [8,]     0     0     0     1     0     1     0     0     0     1     1     0     1
 [9,]     1     1     1     1     0     0     0     0     1     1     0     1     1
[10,]     1     0     1     0     1     1     1     1     1     1     1     1     0
[11,]     0     0     0     1     0     0     1     1     0     0     1     1     0
[12,]     1     1     0     0     0     1     1     0     1     0     1     0     1
[13,]     1     0     1     1     1     1     0     1     0     1     0     0     0
[14,]     1     0     1     1     0     1     1     1     0     1     0     0     1
[15,]     1     0     1     0     0     1     1     1     0     0     0     0     0
[16,]     1     1     1     0     0     1     1     0     0     0     0     1     0
[17,]     0     0     0     0     0     1     1     0     0     0     0     1     1
[18,]     1     0     1     0     1     1     1     1     1     0     0     0     1
[19,]     0     0     1     1     0     0     0     1     0     1     1     1     1
[20,]     0     1     1     0     1     0     1     1     1     0     1     0     1
[21,]     1     1     1     1     1     1     0     1     1     0     0     0     1
[22,]     1     0     1     1     0     0     0     1     1     1     1     0     1
[23,]     1     0     0     1     0     0     0     0     0     0     1     0     1
[24,]     1     1     1     0     1     1     1     1     1     1     0     1     1
[25,]     1     0     0     0     0     0     0     0     0     1     0     0     1
[26,]     1     0     1     0     0     1     1     0     1     1     1     0     0
[27,]     0     1     0     0     1     0     1     1     1     0     0     0     0
[28,]     0     0     0     1     0     1     0     1     1     1     1     1     1
[29,]     0     1     1     0     1     0     1     0     0     1     0     1     1
[30,]     1     1     0     1     0     0     0     1     0     1     0     0     1
      [,28] [,29] [,30]
 [1,]     1     0     1
 [2,]     1     0     1
 [3,]     1     0     0
 [4,]     0     1     0
 [5,]     1     0     0
 [6,]     0     0     0
 [7,]     0     0     1
 [8,]     1     0     1
 [9,]     0     1     0
[10,]     1     0     0
[11,]     1     0     0
[12,]     1     1     0
[13,]     1     1     0
[14,]     1     0     1
[15,]     1     1     0
[16,]     1     1     1
[17,]     1     0     1
[18,]     0     0     0
[19,]     0     0     0
[20,]     1     1     1
[21,]     1     1     1
[22,]     0     1     0
[23,]     0     0     1
[24,]     1     0     1
[25,]     0     0     1
[26,]     1     0     0
[27,]     1     0     1
[28,]     0     0     0
[29,]     1     1     0
[30,]     1     1     1

Calculate the degree of each node using the adjacency matrix:

degree(newmatrix)  # fails since not graph object
Error in degree(newmatrix) : Not a graph object

Degree for directed adjacency matrix:

colSums(newmatrix) + rowSums(newmatrix) - 2*diag(newmatrix) # No self loops
 [1] 30 31 20 19 30 27 30 26 29 28 26 24 29 31 29 26 31 27 25 33 36 32 25 33 29 28 34
[28] 34 26 28

Degree for undirected

colSums(newmatrix) + rowSums(newmatrix) - diag(newmatrix) # No self loops
 [1] 30 32 20 19 31 27 30 26 29 29 26 25 30 31 30 27 31 27 25 33 36 33 25 34 29 28 34
[28] 34 27 29

Convert the adjacency matrix to an asymmetric graph object. Use igraph to calculate the degree of each node.

degree(g3)
 [1] 30 33 20 19 32 27 30 26 29 30 26 26 31 31 31 28 31 27 25 33 36 34 25 35 29 28 34
[28] 34 28 30
DENSITY

What is the density of the dolphin network (two options)?

ecount(dataps2)/((vcount(dataps2) * (vcount(dataps2) - 1))/2)
[1] 0.08141321

What is the density of the directed dolphin data?

graph.density(dataps2d)
[1] 0.04070661

Why is the density this value now? How does it compare to what you found in Question 1?

rr #The density of a directed network is half that of an undirected one because there are twice as many possible edges. In the directed network the density is 0.04070661, which is lower than the density in the undirected network (0.08141321). The directed network density is lower, because when we went from an undirected to a directed network, we are adding edges even though we did not add nodes.

Karate club density, automatically and manually:

graph.density(g)
[1] 0.1390374

Let’s make this clearier to understand what’s happening:

totaledges/possibleedges
[1] 0.1390374

Same, counting directly from the adjacency matrix:

sum(colSums(d))/(nrow(d)*ncol(d))
[1] 0.1349481

Our directed graph:

edges/possibleedges  
[1] 0.1764706

Our undirected graph:

graph.density(g_temp2b)
[1] 0.4571429

Do you notice anything about the density of the directed graph? It is lower. Why is the density of the undirected graph higher than that of the directed one even though they use the same data? Because when we went from undirected to directed adjacency matrices, we added a 1 to the bottom left or top right triangle if it did not exist before. So, we are adding edges even though we are not adding nodes.

Generate a 10 x 10 matrix.

rr number <- 10 thismatrix <- matrix(rbinom(number^2, 1, .5), nrow=number)

Calculate its directed density.

tm_d <- graph_from_adjacency_matrix(thismatrix, mode='directed')
tm_d <- graph_from_adjacency_matrix(thismatrix, mode='directed')
graph.density(tm_d)
[1] 0.6111111
edges <- sum(thismatrix)
possibleedges <- nrow(thismatrix)*ncol(thismatrix)-nrow(thismatrix)
edges/possibleedges 
[1] 0.6111111

Calculate its undirected density. Remember, the calculation is the same once you define the edges correctly. First, make symmetric before passing to igraph and remove self loops.

graph.density(tm_u, loops=FALSE)
[1] 0.4888889

CLUSTERING

What is the global clustering of the undirected dolphin data (two options)?

transitivity(dataps2, type = "global")
[1] 0.3087757

What is the node with the highest local clustering value (two options)?

V(data)[V(data)$clustering == clusteringMax]
+ 0/62 vertices, from 294c26f:

Note that one can specificy undirected but it does not change the calculation; igraph does not appear to calculate directed clustering, globally or locally:

Calculate the global transitivity for our directed graph. transitivity(tm_d, type=‘global’)

Calculate the local transitivity for our directed graph. transitivity(tm_d, type=‘local’)

Calculate the global transitivity for our undirected graph. transitivity(tm_u, type=‘global’) transitivity(tm_u, type=‘globalundirected’)

Calculate the local transitivity for our undirected graph. transitivity(tm_u, type=‘local’) transitivity(tm_u, type=‘localundirected’)

Find the node with the highest clustering coefficient for the directed network. V(tm_d)[transitivity(tm_d, type=‘local’) == max(transitivity(tm_d, type=‘local’))]

mt <- max(transitivity(tm_d, type=‘local’)) V(tm_d)[transitivity(tm_d, type=‘local’) == mt]

Find the node with the lowest clustering coefficient undirected network. V(tm_u)[transitivity(tm_u, type=‘local’) == min(transitivity(tm_u, type=‘local’))]

mint <- min(transitivity(tm_u, type=‘local’)) V(tm_u)[transitivity(tm_u, type=‘local’) == mint]

Plot the relationship between degree and clustering for the 10x10 undirected network. plot(degree(tm_u), transitivity(tm_u, type=‘local’), xlab=‘Node Degree’, ylab=‘Local Clustering’, pch=20)

RANDOM NETWORK

Generate a random network containing 200 nodes with a probability of .2 that any two nodes are connected. Use igraph to convert it to an undirected matrix. Make a histogram of the degree distribution, label the axes, give it a title, and save it to file (two options).

Repeat Problem 1, except vary the probability of ties from .1 to 1 in increments of .1. Save each histogram in a file. What do you notice as p changes?

rr nodes <- 200 prob <- seq(.1,1,by=.1)

for(i in 1:length(prob)){ thenetwork <- matrix(rbinom(nodes^2, 1, p=prob[i]), nrow=nodes) diag(thenetwork) <- 0 thenetwork <- graph_from_adjacency_matrix(thenetwork, mode=‘upper’)

pdf(paste0(‘~/Desktop/UCLA’, prob[i], ‘.pdf’)) hist(degree(thenetwork), xlab=‘Degree’, ylab=‘Count’, main=paste0(‘Distribution of Degree, p=’, prob[i]), breaks=50) dev.off() }

#Making Symmetric Networks From an Adjacency Matrix

Convert an unbalaned adjacency matrix to an undirected adjacency matrix. Start by making the matrix:

rr probability <- .2 thisnumber <- 12 thismatrix <- matrix(rbinom(thisnumber^2, 1, probability), nrow=thisnumber) diag(thismatrix) <- 0

Convert it to an undirected network.

t(thismatrix) 
      [,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8] [,9] [,10] [,11] [,12]
 [1,]    0    0    0    1    0    1    1    0    0     1     0     0
 [2,]    0    0    1    0    0    0    0    0    1     0     0     0
 [3,]    0    0    0    1    0    0    0    1    0     0     0     0
 [4,]    0    0    0    0    0    0    0    0    0     0     0     0
 [5,]    0    0    0    0    0    0    0    0    1     0     0     0
 [6,]    0    0    0    0    1    0    0    0    0     0     0     0
 [7,]    0    0    0    1    1    0    0    0    0     0     0     0
 [8,]    0    0    0    0    0    0    0    0    0     0     0     0
 [9,]    0    1    0    0    0    1    0    1    0     0     0     1
[10,]    0    0    0    0    0    0    0    0    0     0     0     1
[11,]    0    0    0    0    0    1    0    0    0     0     0     0
[12,]    0    0    0    0    0    0    0    1    0     0     0     0

RANDOM GRAPH

Build random graphs from matrices. Then, we use igraph to build a random graph. In both cases, we will learn about the network via some statistics. At the end of this section, we will learn how to automatically vary parameters and view the statistics of the resulting networks. Create using matrix.

howmanyrows <- 100
temp <- matrix(rbinom(howmanyrows^2, 1, .5), nrow=howmanyrows)
diag(temp) <- 0  # Make sure there are no self-loops.

# Do not forget to make symmetric
temp[lower.tri(temp)] <- t(temp)[lower.tri(temp)]

bus720 <- graph_from_adjacency_matrix(temp, mode='undirected')  # I have chosen a weird object name to emphasize that they are arbitrary.  Most documentation for igraph calls graph objects g, but you do not have to.

Explore degree:

summary(degree(bus720))
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
  36.00   46.00   50.00   49.66   53.00   66.00 

Explore clustering:

transitivity(bus720, type="local")[1:10]
 [1] 0.5206349 0.5132979 0.5063425 0.4804878 0.4964706 0.4956140 0.5130435 0.5045455
 [9] 0.5175510 0.4927536

Explore density:

graph.density(bus720)
[1] 0.5016162

Now, let us learn how to have igraph make a random graph for us. I will then show how you can vary the density of the graph using a loop; you could do the same for nodes, but I leave that exercise for the problem set. I will then show how to put the output from each variation into a dataframe; otherwise, you lose the results as each new loop runs. Create using igraph package, explore degree, clustering, density.

graph.density(ibus720)
[1] 0.4923232

SMALL-WORLD NETWORKS

Recreate the small-world chart I showed in class, the one that shows how average path length and clustering change as the probability of rewiring edges increases. Make the network a ring with 1,000 nodes where each node is connected to ten neighbors. As you work on this problem, keep in mind six things: a. You will need a baseline model by which to normalize (divide) the values of each simulation. That is, you will need a model that does not change for each iteration of the loop. b. Make sure you have very small values of p, smaller than the class example. c. Make sure the x-axis is the logarithm of p. 1. If one of your values of p is 0, you should add a small amount, such as .01 or .1, to each value of the rewiring probability when you plot. log(0) is undefined, but you need to see the result for that value. 2. When you do this, you may notice the x-axis values become negative. It is acceptable if you leave the values like that, but I encourage you to explore how to fix them. Subhint: look at xaxt in plot() and axis() after plot(). Subhint2: You could also look at the log argument in plot(). d. To add a second line to a graph, use lines() after plot(). e. Save the chart to your computer. f. How will you distinguish which line corresponds to which statistic? Use the lty argument to lines() and plot(). If you are adventurous, add a legend to the plot as well.

Use igraph: From here on out, we will rely on igraph’s functions to build networks. While we could build them manually from adjacency matrices, creating the proper adjacency matrix or edgelist is a bit more complicated. It is doable but not necessary for the aims of this class. The igraph function sample_smallworld can be confusing. You define the number of dimensions of the lattice; 1 means a line, 2 means a matrix, and so on. The dimension will raise the number of nodes you want to the power of the dimension. It is therefore easiest to just define the number of nodes you want and assume one dimension. nei is how far (many steps) in each direction to make connections between nodes; the higher it is, the greater the average degree.

Create a small-worl network using igraph package, explore degree, clustering, density:

graph.density(bus20)
[1] 0.06060606

Let’s make the printed output a little easier to read.

paste('The density of the graph is', graph.density(bus20))
[1] "The density of the graph is 0.0606060606060606"

Let’s do that again, changing the size of the network fro 100 to 1,000.

rr nodes <- 1000

bus20 <- sample_smallworld(dim=1, size=nodes, nei=3, p=.2)

Let’s make the printed output a little easier to read.

summary(degree(bus20))
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
      1       5       6       6       7      12 
paste('The graph\'s transitivity is', round(transitivity(bus20, type='global'), 5))
[1] "The graph's transitivity is 0.16376"
paste('The other way to calculate transitivity suggests the value is', mean(transitivity(bus20, type='local')))
[1] "The other way to calculate transitivity suggests the value is NaN"
paste('The other way to calculate transitivity suggests the value is', mean(transitivity(bus20, type='local'), na.rm=TRUE))
[1] "The other way to calculate transitivity suggests the value is 0.185631518964852"
paste('The density of the graph is', graph.density(bus20))
[1] "The density of the graph is 0.00600600600600601"
paste('There are', ecount(bus20), 'edges in the graph')
[1] "There are 3000 edges in the graph"

Now, we can vary the parameters like we did for random graphs. Since we are familiar with this process, I will vary p and save the results to a dataframe immediately. Notice that I define how many iterations of the loop differently than for random graphs; they both work the same. I am going to introduce a new way to generate sequences of numbers as well.

Vary p using sequence. There are two ways to do this.

Why does the density never change? Because it is the percentage of possible edges that actually exist; density tells you about the network overall (while degree tells you something about individual nodes)

Create a small-world ring network with 100 nodes that each start with connections to 10 neighbors. Randomly rewire with probability of .03.

sw
IGRAPH 77a5caa U--- 100 500 -- Watts-Strogatz random graph
+ attr: name (g/c), dim (g/n), size (g/n), nei (g/n), p (g/n), loops (g/l),
| multiple (g/l)
+ edges from 77a5caa:
 [1]  1-- 2  2-- 3  3-- 4  4-- 5  5-- 6  7--74  7-- 8  8-- 9 10--82 10--11 11--12
[12] 12--13 13--14 14--15 15--16 16--17 17--18 18--19 19--20 20--21 21--22 22--23
[23] 23--24 24--25 25--26 26--27 27--28 28--29 29--30 30--31 14--32 32--33 33--34
[34] 34--35 35--36 36--37 24--37 38--39 39--40 40--41 41--42 42--43 43--44 44--45
[45] 45--46 46--47 47--48 48--49 49--50 50--51 51--52 52--53 53--54 54--55 55--56
[56] 56--57 57--58 58--59 59--60 60--61 61--62 62--63 63--64 64--65 65--66 66--67
[67] 67--68 68--69 69--70 70--71 71--72 72--73 73--74 74--75 18--75 77--96 77--78
+ ... omitted several edges

Write a loop that creates a new small-world network each iteration. Vary the rewiring probability from 0 to .2 in increments of .01. Save the results in a dataframe, tracking at least the overall clustering, diameter, and average path length.

SCALE FREE NETWORKS

Create a scale free network with 12,000 nodes where each node adds 20 edges.
a. Make a histogram of the degree distribution. b. What is the ratio of the mean to median degree? c. Is the density less than a small-world network? Why?

graph.density(sf)
[1] 0.003330694

Now, we will explore scale-free networks, using Barabasi’s model of preferential attachment. While there are many mechanisms that can generate scale-free distributions, his is the most commonly used and therefore the one most commonly included out of the box in software. We will focus on varying the number of edges added to a network. A key feature of scale-free networks is that the mean degree is much higher than the median; we will track that ratio.

degree_distribution(sf)
  [1] 0.000000e+00 0.000000e+00 0.000000e+00 0.000000e+00 0.000000e+00 0.000000e+00
  [7] 0.000000e+00 0.000000e+00 0.000000e+00 0.000000e+00 1.615000e-01 1.225833e-01
 [13] 9.866667e-02 7.875000e-02 6.375000e-02 5.725000e-02 4.841667e-02 3.783333e-02
 [19] 3.300000e-02 2.925000e-02 2.550000e-02 2.066667e-02 1.966667e-02 1.608333e-02
 [25] 1.325000e-02 1.308333e-02 9.833333e-03 1.033333e-02 1.000000e-02 8.416667e-03
 [31] 8.416667e-03 6.750000e-03 6.083333e-03 5.666667e-03 5.916667e-03 5.916667e-03
 [37] 4.750000e-03 3.833333e-03 4.000000e-03 2.666667e-03 2.250000e-03 2.166667e-03
 [43] 3.250000e-03 2.416667e-03 2.916667e-03 2.333333e-03 2.166667e-03 2.666667e-03
 [49] 2.083333e-03 1.750000e-03 7.500000e-04 1.250000e-03 2.250000e-03 1.916667e-03
 [55] 1.750000e-03 1.416667e-03 1.000000e-03 1.250000e-03 8.333333e-04 5.000000e-04
 [61] 1.500000e-03 1.083333e-03 9.166667e-04 8.333333e-04 4.166667e-04 8.333333e-04
 [67] 5.000000e-04 6.666667e-04 7.500000e-04 5.000000e-04 5.833333e-04 5.000000e-04
 [73] 2.500000e-04 9.166667e-04 5.833333e-04 2.500000e-04 5.833333e-04 4.166667e-04
 [79] 4.166667e-04 5.000000e-04 8.333333e-04 4.166667e-04 5.000000e-04 2.500000e-04
 [85] 5.000000e-04 3.333333e-04 5.000000e-04 4.166667e-04 4.166667e-04 5.000000e-04
 [91] 2.500000e-04 2.500000e-04 1.666667e-04 5.000000e-04 3.333333e-04 8.333333e-05
 [97] 3.333333e-04 4.166667e-04 2.500000e-04 8.333333e-05 3.333333e-04 3.333333e-04
[103] 1.666667e-04 1.666667e-04 5.833333e-04 2.500000e-04 8.333333e-05 8.333333e-05
[109] 3.333333e-04 0.000000e+00 0.000000e+00 1.666667e-04 4.166667e-04 1.666667e-04
[115] 2.500000e-04 1.666667e-04 8.333333e-05 8.333333e-05 8.333333e-05 0.000000e+00
[121] 0.000000e+00 0.000000e+00 8.333333e-05 0.000000e+00 0.000000e+00 0.000000e+00
[127] 8.333333e-05 1.666667e-04 1.666667e-04 8.333333e-05 1.666667e-04 1.666667e-04
[133] 1.666667e-04 1.666667e-04 8.333333e-05 1.666667e-04 8.333333e-05 8.333333e-05
[139] 0.000000e+00 1.666667e-04 0.000000e+00 2.500000e-04 0.000000e+00 0.000000e+00
[145] 0.000000e+00 0.000000e+00 0.000000e+00 8.333333e-05 0.000000e+00 1.666667e-04
[151] 0.000000e+00 8.333333e-05 0.000000e+00 8.333333e-05 8.333333e-05 0.000000e+00
[157] 2.500000e-04 8.333333e-05 0.000000e+00 0.000000e+00 8.333333e-05 0.000000e+00
[163] 0.000000e+00 0.000000e+00 8.333333e-05 8.333333e-05 8.333333e-05 0.000000e+00
[169] 0.000000e+00 0.000000e+00 8.333333e-05 0.000000e+00 0.000000e+00 0.000000e+00
[175] 0.000000e+00 0.000000e+00 0.000000e+00 8.333333e-05 8.333333e-05 0.000000e+00
[181] 0.000000e+00 8.333333e-05 0.000000e+00 0.000000e+00 0.000000e+00 8.333333e-05
[187] 0.000000e+00 0.000000e+00 0.000000e+00 8.333333e-05 0.000000e+00 0.000000e+00
[193] 8.333333e-05 0.000000e+00 8.333333e-05 0.000000e+00 1.666667e-04 0.000000e+00
[199] 0.000000e+00 0.000000e+00 0.000000e+00 8.333333e-05 1.666667e-04 8.333333e-05
[205] 0.000000e+00 0.000000e+00 0.000000e+00 8.333333e-05 0.000000e+00 0.000000e+00
[211] 0.000000e+00 8.333333e-05 0.000000e+00 0.000000e+00 0.000000e+00 0.000000e+00
[217] 1.666667e-04 0.000000e+00 0.000000e+00 0.000000e+00 0.000000e+00 0.000000e+00
[223] 0.000000e+00 0.000000e+00 0.000000e+00 0.000000e+00 0.000000e+00 8.333333e-05
[229] 8.333333e-05 0.000000e+00 0.000000e+00 8.333333e-05 0.000000e+00 0.000000e+00
[235] 0.000000e+00 0.000000e+00 0.000000e+00 0.000000e+00 0.000000e+00 0.000000e+00
[241] 0.000000e+00 8.333333e-05 0.000000e+00 8.333333e-05 0.000000e+00 0.000000e+00
[247] 0.000000e+00 0.000000e+00 0.000000e+00 0.000000e+00 8.333333e-05 0.000000e+00
[253] 0.000000e+00 0.000000e+00 0.000000e+00 8.333333e-05 0.000000e+00 0.000000e+00
[259] 0.000000e+00 8.333333e-05 0.000000e+00 0.000000e+00 0.000000e+00 0.000000e+00
[265] 0.000000e+00 0.000000e+00 0.000000e+00 0.000000e+00 0.000000e+00 8.333333e-05
[271] 0.000000e+00 0.000000e+00 0.000000e+00 0.000000e+00 0.000000e+00 0.000000e+00
[277] 8.333333e-05 0.000000e+00 0.000000e+00 0.000000e+00 8.333333e-05 0.000000e+00
[283] 0.000000e+00 0.000000e+00 0.000000e+00 8.333333e-05 0.000000e+00 0.000000e+00
[289] 0.000000e+00 0.000000e+00 0.000000e+00 0.000000e+00 0.000000e+00 0.000000e+00
[295] 0.000000e+00 0.000000e+00 1.666667e-04 0.000000e+00 0.000000e+00 0.000000e+00
[301] 0.000000e+00 0.000000e+00 8.333333e-05 0.000000e+00 0.000000e+00 0.000000e+00
[307] 0.000000e+00 0.000000e+00 0.000000e+00 0.000000e+00 0.000000e+00 0.000000e+00
[313] 0.000000e+00 0.000000e+00 0.000000e+00 0.000000e+00 0.000000e+00 0.000000e+00
[319] 0.000000e+00 0.000000e+00 0.000000e+00 0.000000e+00 0.000000e+00 0.000000e+00
[325] 0.000000e+00 0.000000e+00 0.000000e+00 8.333333e-05 0.000000e+00 0.000000e+00
[331] 0.000000e+00 0.000000e+00 8.333333e-05 0.000000e+00 0.000000e+00 0.000000e+00
[337] 0.000000e+00 0.000000e+00 0.000000e+00 0.000000e+00 0.000000e+00 0.000000e+00
[343] 0.000000e+00 0.000000e+00 0.000000e+00 1.666667e-04 0.000000e+00 0.000000e+00
[349] 0.000000e+00 0.000000e+00 0.000000e+00 0.000000e+00 0.000000e+00 0.000000e+00
[355] 0.000000e+00 0.000000e+00 0.000000e+00 0.000000e+00 0.000000e+00 0.000000e+00
[361] 0.000000e+00 0.000000e+00 0.000000e+00 0.000000e+00 0.000000e+00 0.000000e+00
[367] 0.000000e+00 0.000000e+00 0.000000e+00 8.333333e-05 0.000000e+00 0.000000e+00
[373] 0.000000e+00 0.000000e+00 0.000000e+00 0.000000e+00 0.000000e+00 0.000000e+00
[379] 0.000000e+00 0.000000e+00 8.333333e-05 0.000000e+00 0.000000e+00 8.333333e-05
[385] 0.000000e+00 0.000000e+00 0.000000e+00 0.000000e+00 0.000000e+00 0.000000e+00
[391] 0.000000e+00 0.000000e+00 0.000000e+00 0.000000e+00 0.000000e+00 0.000000e+00
[397] 0.000000e+00 0.000000e+00 0.000000e+00 0.000000e+00 0.000000e+00 0.000000e+00
[403] 0.000000e+00 0.000000e+00 0.000000e+00 0.000000e+00 0.000000e+00 8.333333e-05
[409] 0.000000e+00 8.333333e-05 0.000000e+00 0.000000e+00 0.000000e+00 0.000000e+00
[415] 0.000000e+00 0.000000e+00 0.000000e+00 0.000000e+00 8.333333e-05 0.000000e+00
[421] 0.000000e+00 0.000000e+00 0.000000e+00 0.000000e+00 0.000000e+00 0.000000e+00
[427] 0.000000e+00 0.000000e+00 0.000000e+00 0.000000e+00 0.000000e+00 0.000000e+00
[433] 0.000000e+00 0.000000e+00 0.000000e+00 8.333333e-05 0.000000e+00 0.000000e+00
[439] 0.000000e+00 0.000000e+00 0.000000e+00 0.000000e+00 0.000000e+00 0.000000e+00
[445] 0.000000e+00 0.000000e+00 0.000000e+00 8.333333e-05 0.000000e+00 0.000000e+00
[451] 0.000000e+00 0.000000e+00 0.000000e+00 0.000000e+00 0.000000e+00 0.000000e+00
[457] 0.000000e+00 0.000000e+00 0.000000e+00 0.000000e+00 0.000000e+00 0.000000e+00
[463] 0.000000e+00 0.000000e+00 0.000000e+00 0.000000e+00 8.333333e-05 0.000000e+00
[469] 0.000000e+00 0.000000e+00 0.000000e+00 0.000000e+00 0.000000e+00 0.000000e+00
[475] 0.000000e+00 0.000000e+00 8.333333e-05

Let’s make the printed output a little easier to read.

Now, let’s use a loop to see how these measures change as we increase the number of nodes in the network. Let us increase it in units of 1,000. Vary network size using a loop:

nodes <- seq(from=1000,to=10000,by=1000)

remember <- data.frame(iteration=NULL, networkSize=NULL, avgDegree=NULL, globalClustering=NULL, glbalClustering2=NULL, density=NULL, ratio=NULL, avgPathLength=NULL)

for(i in 1:length(nodes)){
  temp <- sample_pa(n=nodes[i], power=1, m=10, directed=FALSE)
  
  print(paste('This is loop', i, 'of', length(nodes)))
  
  d <- data.frame(iteration=i, size=nodes[i], avgDegree=mean(degree(temp)), globalClustering=transitivity(temp, type='global'), globalClustering2=mean(transitivity(temp, type='local')), density=graph.density(temp), ratio=mean(degree(temp))/median(degree(temp)), avgPathLength=average.path.length(temp))

  remember <- rbind(remember, d)  # "rowbind", adds a new row, d, to whatever exists in remember.
}

remember # Not bad.

REAL NETWORKS

In this section, we will explore actual network data. The first dataset is e-mails from Enron, an energy firm that imploded when its accounting manipulations were discovered. The second is a sample of individuals from Facebook.

Load Enron:

ecount(enron)
[1] 116865
summary(degree(enron2))
    Min.  1st Qu.   Median     Mean  3rd Qu.     Max. 
   0.000    0.000    1.000    6.375    2.000 1367.000 
paste('The graph\'s transitivity is', transitivity(enron2, type='global'))
[1] "The graph's transitivity is 0.0715762061104356"
paste('The other way to calculate transitivity suggests the value is', mean(transitivity(enron2, type='local')))
[1] "The other way to calculate transitivity suggests the value is NaN"
paste('The density of the graph is', graph.density(enron2))
[1] "The density of the graph is 0.000173859823993874"
paste('There are', ecount(enron2), 'edges in the graph')
[1] "There are 116865 edges in the graph"
paste('The diameter is', diameter(enron2))  # Takes awhile to run.
[1] "The diameter is 7"
paste('The average path length is', average.path.length(enron2))  
[1] "The average path length is 3.5201731990681"

We can also take subsets of a network based on node features. Below, I will show you how to keep only nodes meeting degree thresholds.

vcount(degree10)/vcount(enron2)  # 8.94% of nodes
[1] 0.08940163
ecount(degree10)/ecount(enron2)  # 62.45% of edges
[1] 0.6245497

You can keep nodes that match any sort of filter you want. Next, I show based on clustering. Show how to select nodes with high clustering

table(degree(highclustering_g))  

   0    1    2    3    4    5    6    7    8    9 
7462  364   95   54   37   10   13    2    2    2 

CALCULATIONS (vectors, objects, phrases)

What is 3819 times 102?

rr 3819 * 102

What is 3819 minus 1892?

rr 3819 - 1892

Repeat the above, but save each number as an object.

rr obj1 <- 3819 obj2 <- 1892 obj1 * obj2 obj1 - obj2

Make a vector of the numbers 10, 20, 30, 40, and 50. Multiply that vector # by another of the numbers 3, 4, 5, 6, and 7.

rr v1 <- c(10, 20, 30, 40, 50) v1_alt <- seq(from = 10, to = 50, by = 10) v2 <- c(3, 4, 5, 6, 7) v2_alt <- 3:7

v1 * v2 v1_alt * v2_alt

Repeat above, except make the second vector the numbers 5 and 10. Show the results and explain, in words, what R is doing with this shorter vector.

rr v2 <- c(5, 10) v1 * v2

#(Warning in v1 * v2: longer object length is not a multiple of shorter object length): R repeats the v2 vector as many times as it takes to match the length of v1. So, it creates a vector that is (5,10,5,10,5)

Does the phrase, “I enjoy learning to program” contain the word “learning”? What about “learn”? What about “towards”?

rr > phrase <- c(‘I enjoy learning to program’) > grepl(‘learning’, phrase) [1] TRUE > grepl(‘learn’, phrase) [1] TRUE > grepl(‘towards’, phrase) [1] FALSE

Why does R return [1]?

rr #Because it is telling the result is a vector of length 1. Vectors and calculation become especially useful when your vector has length greater than 1, such as when it represents a variable with multiple observations. You can then easily manipulate all entries in one vector or perform operations on multiple vectors. To make a vector of length greater than 1, you need c(). The stands for .

Practice with vectors of length greater than 1:

rr first <- c(100,19,20,89,76,102,74,81,65) first * 2
#This multiplies each entry by 2 (great to convert inches to centimeters) first - 20
#This subtracts each entry by 20

second <- c(1,2,4,10,10,1,4,5,10) #Do not need to have the exact same number of entries again, but made it more simple)

first * second
#Multiplies each entry in first by second in order, ie 1*100; multiplying each entry is weighting data first / second

Example using vectors to calculate a more complicated outcome:

rr observed <- first # I have created a new object that has the values of first, i.e., is mapped to first above.

observed == first
# Returns TRUE for as many entries as there are in each vector. (Need two equal marks, one equal will just make a copy like <-, while this compares, good way to check data after doing a transformation)

observed != first #(booleian operation for does not equal)

Chi-square

rr expected <- c(90,18,30,79,80,88,80,83,70) numerator <- (observed-expected)^2 sum(numerator/expected)

Vectors do not have to be numbers. For example, you may have a variable that is a respondent’s favorite sports team, so the entries are likely to be words. If you have a dataframe, the column names are a string vector:

rr char_vec <- c(, , , ) length(char_vec)

‘words’ %in% char_vec ‘words’ == char_vec #for how often ‘word’ %in% char_vec #^good for searching larger texts

sametype <- c(‘this’, 20, ‘converts to’, 5, ‘strings’) #^if R is unsure of a thing it converts it into a string

Create an object that contains five numbers. Multiply those numbers by 2. Add 82 to those numbers.

rr this <- c(1,2,3,4,5) this*2 this+82

Create a second object that contains five numbers. Multiply the two objects together and save the result as a new object. Divide the two objects, saving the result as a new object.

rr arbitrary <- c(18,18,93,103, 45) new <- this*arbitrary new <- this/arbitrary

Does the vector contain the word “tomato”?

rr characters <- c(‘this’, ‘vector’, ‘contains’, ‘words’, ‘that’, ‘have’, ‘words’, ‘with’, ‘letters’, ‘and’, ‘numbers’, 29, ‘30’ )

‘tomato’ %in% characters

How many times does the vector contain the word “words”?

rr grep(‘words’, characters) length(grep(‘words’, characters))

Search the vector for the number 29.

rr grep(‘29’, characters)

Search the vector for the number 100.

rr grep(‘100’, characters)

#integer(0) –> way R says no

It works differently if the words are not separated in a vector:

rr words <- c(‘this vector contains words that have words with letters and numbers 29 30’) ‘this’ %in% words

#FALSE because the word this does not = the first unit in vector words

length(words)

grep(‘this’, words) grepl(‘this’, words)

Notice that grep partial matches by default. To get exact word, use spaces; grep will tell me anywhere vs %in% which is an exact match

grepl(‘contain’, words) grepl(‘contain’, words)

Note: You cannot give a vector a number, e.g., in scores[2,4], R is looking for the second row and fourth column; would need to write:

rr scores <- c(John=25, Marge=34, Dan=24, Emily=29) scores[c(2,4)]

Return individuals with scores greater than 30:

rr scores[scores > 30]

DATA STRUCTURES (create a list, matrix, dataframe, identify variables)

Name each vector in the list:

rr mary_info <- list(classes=c(, , , ), friends=c(, , ), SAT=c(1450,1000,1600))

Return the second vector:

rr mary_info[[2]]

Return the vector named “SAT”

rr mary_info$SAT

Return the second element of friends vector

rr mary_info$friends[2]

Make a list that contains the three departments at Luskin as well as the number of enrolled students in each. Assume the numbers are 60, 80, and 100.

rr list0 <- list(depts = c(, , ), enrolled = c(60 80, 100))

For these next questions, use the list created in the below code.

rr list1 <- list(names=c(‘John’, ‘Kate’, ‘Jessica’, ‘Carl’), grades=c(80,85,60,100), sex=c(‘Male’, ‘Female’, ‘Female’, ‘Male’))

Who has grades greater than 83?

rr list1\(names[list1\)grades > 83]

Which people are female?

rr list1\(names[list1\)sex == ]

Create a 2x3 matrix, filling down columns:

rr a <- matrix(1:6, nrow=2)

Fill across rows:

rr b <- matrix(5:14, nrow=2, byrow=TRUE)

Row 2, column 3:

rr a[2,3]

All rows, column 2:

rr b[,2]

All columns, row 1:

rr a[1,]

Other ways to create matrices:

rr matrix(c(0,1,1,1,0,1,0,0,1), nrow=3)

matrix(0:1, nrow=10, ncol=10)

Create a matrix of 10 rows and 10 columns where each entry is the number 0 or 1 (multiple options).

rr m1 <- matrix(rbinom(100, 1, 0.5), nrow = 10) m <- matrix(0:1, nrow=10, ncol=10)

Show me the first row of the matrix.

rr m1[1, ]

Show the first six rows of the matrix (two options).

rr head(m1) m1[1:6, ]

Show rows 4 through 8.

rr m1[4:8, ]

Show rows 3, 5, 7, and 9.

rr m1[c(3, 5, 7, 9), ]

Show the third column of the matrix.

rr m1[, 3]

Show the third, fifth, and seventh columns of the matrix.

rr m1[, c(3, 5, 7)]

Convert the grades list from above into a data frame (four options).

rr d1 <- data.frame(list1)

as.data.frame(list1)

d1b <- data.frame(names=c(‘John’, ‘Kate’, ‘Jessica’, ‘Carl’), grades=c(80,85,60,100), sex=c(‘Male’, ‘Female’, ‘Female’, ‘Male’))

data.frame(list1)

Who has grades greater than 90?

rr d1[d1$grades > 90, ]

Which people are male?

rr d1[d1$sex == , ]

What are the first six rows and nine columns in the SCAD data?

rr data[1:6, 1:9]

What are the last five rows in the SCAD data (two options)?

rr tail(data, 5) data[(nrow(data)-5):nrow(data),]

Show rows 100 to 107 in the SCAD data.

rr data[100:107, ]

What are the names of the columns in the SCAD data?

rr names(data)

How many times does each country appear in the dataset using ‘table’ in the SCAD data?

table(SCADdata$countryname)

         Costa Rica                Cuba  Dominican Republic         El Salvador 
                 35                 278                 214                 135 
          Guatemala               Haiti            Honduras             Jamaica 
                348                1322                 214                 275 
             Mexico           Nicaragua              Panama Trinidad and Tobago 
               1905                 165                  97                  45 

How many events started in each year?

rr table(data\(styr) table(data\)eyr)

Write code to save only events that started in 2006 as a new object.

rr new <- data[data$styr == 2006, ]

How many events started in 2006?

rr nrow(new)

Make a table showing the number of events per country that started in 2006.

rr table(new$countryname)

Drop all events where the number of participants is -99.

rr new2 <- data[data$npart != -99, ]

Create a logical vector and numeric vector of equal length:

rr mydata <- data.frame(diabetic = c(TRUE, FALSE, TRUE, FALSE), height = c(65, 69, 71, 73), sex=c(‘Male’, ‘Female’, ‘Female’, ‘Male’), minutesExercise = c(20,30,22,80))

Identify row 3, column 2 height of third male:

rr mydata[3,2]

Using column name, identify the first two rows:

rr mydata[1:2, ]

List all rows of column “diabetic”:

rr mydata[,]

Subsetting creates a numeric vector:

rr mydata$height[2:3]

Numeric vector:

rr mydata[[]]

What are the names of my variables?

rr colnames(mydata)

Assign column names:

rr colnames(mydata) <- c(, , , of Exercise per Week)

#Prof recs alphabetizing

To change the first variable’s name, just use indexing:

rr colnames(mydata)[1] <-

#to verify: colnames(mydata) == ‘Diabetes’

#If you do not now the index but know the name, you can do that too: colnames(mydata)[colnames(mydata) == ‘Diabetes’] <- ‘Diabetic’
#^Where the column name is , change it to .

What are the dimensions of my data frame?

rr dim(mydata)

What are the first five rows?

rr head(mydata)

What are the last five rows?

rr tail(mydata)

Get information about the dataframe, structure:

rr str(mydata)

Get summary statistics:

rr summary(mydata)

summary(mydata$height)

Get number of rows:

rr nrow(mydata)

#Don’t select on col numbers, they won’t stay the same; use colnames(mydata)[colnames(mydata) == ‘Diabetes’] (can also use grep)

Create a dataframe that is five rows tall and three columns wide. The first column is people’s names: Amanda, Brandon, Carl, David, and Eleanor. The second column is their birth year: 1981, 1928, 1997, 2002, 1989. The third column is their height in centimeters: 200, 180, 176, 190, 210.

rr mydata <- data.frame(diabetic = c(TRUE, FALSE, TRUE, FALSE), height = c(65, 69, 71, 73), sex=c(‘Male’, ‘Female’, ‘Female’, ‘Male’), minutesExercise = c(20,30,22,80))

d2 <- data.frame(names=c(‘Amanda’, ‘Brandon’, ‘Carl’, ‘David’, ‘Eleanor’), year=c(1981, 1928, 1997, 2002, 1989), height = c(200,180,176,190,210))

Select rows three through five of the dataframe.

rr d2[3:5,]

Rename the name column to “First_Name”. Rename the birthyear column to “Year”.

rr colnames(d2)[colnames(d2)==‘names’] <- ‘First_Name’ colnames(d2)[colnames(d2)==‘year’] <- ‘Year’

Create a new dataframe of only the individuals who are at least 200 centimeters tall.

rr dtall <- d2[d2$height > 200,]

Create a four row, four column matrix. Multiple that matrix by 3.

rr m <- matrix(1:3, nrow=4, ncol=4) m2 <- matrix(1:16, nrow=4)

m2*3

From that matrix, take the sum of the first column. Take the sum of the third row.

rr sum(m2[,1]) m2[,1] temp <- m2[,1] sum(temp)

sum(m2[3,])

How many columns? ncol(data)

How many rows? nrow(data)

What are the column names? names(data)

What are the row names? rownames(data)

What are the first 6 rows? head(data)

What are the last 6 rows? tail(data)

Note that you can tell head() and tail() how many rows you want to see. 5 is the default. head(data, 10) head(data, 2)

tail(data, 8) tail(data, 2)

If you want to know the set of values a variable takes, use unique() unique(data\(COUNTRY) unique(data\)BYEAR)

If you want to get a sense of how many observations there are for each value of a variable, use table() table(data\(COUNTRY) table(data\)BYEAR)

Explore how to access different data points, based on variable values. We will then modify some of these variables and save the result to a new file.

rr # Select by year.
temp <- data[data$BYEAR == ‘2010’,]
# The events from 2010 are now in this dataframe dim(temp) nrow(temp) - nrow(data)

temp <- data[data$BYEAR == ‘2011’,]
# Now, the  dataframe is for 2011.

temp <- data[data$BYEAR < 2010,]
temp2 <- data[data$BYEAR <= 2010,]

nodeaths <- data[data$NDEATH == 0,] january <- data[data$BMONTH == 1,] notjanuary <- data[data$BMONTH != 1,] # != means equal to. notjanuary2 <- data[data$BMONTH > 1,]

Manipulate variables and then save the dataframe. Remember, each column is just a vector, so the vector operations we learned earlier are what we will do here. Note that I do some transformations, like multiple BDAY by BMONTH, just to demonstrate capabilities.

rr data\(newnpart <- data\)NPART*10 data\(newnpart2 <- ifelse(data\)NPART != 99, data\(NPART, NA) data\)npart <- data$NPART/1000
names(data) # Notice that the above line created a new variable.

data\(NPART <- data\)NPART/1000
# Creating a new variable with the same name as an existing one replaces that variable

data\(deathRate <- data\)NDEATH/data\(NPART # What does this do? summary(data\)deathRate) # It did nothing because NDEATH is weirdly constructed.
# We will not discuss here how to fix it.

data\(nonsense <- data\)BDAY*data\(BMONTH data\)COUNTRY[data$COUNTRY == ‘Cote d'Ivoire’] <- ‘Ivory Coast’
# Fails because R treats country name as a factor.

Load the dataframe from earlier. Add the argument `stringsAsFactors=FALSE’.

rr data <- read.csv(‘~/Documents/UCLA/Data/PRIO_UrbanSocialDisorder/events.csv’, stringsAsFactors = FALSE) data\(COUNTRY[data\)COUNTRY == ‘Cote d'Ivoire’] <- ‘Ivory Coast’

How could you verify that the change happened?

rr # Save the file. Make sure to change the file path for your computer. write.csv(data, ‘~/Documents/UCLA/Data/PRIO_UrbanSocialDisorder/events_v3.csv’)

write.table(data, ‘~/Documents/UCLA/Data/PRIO_UrbanSocialDisorder/events_v3.txt’, sep=’)

What are the first five rows?

rr head(mtcars, 5) mtcars[1:5,]

What are the last five rows?

rr tail(mtcars, 5)

What are rows 12 through 21?

rr mtcars[12:21,]

What are the column names?

rr names(mtcars) sort(names(mtcars))

Using mtcars: create a new variable that is horsepower per cylinder.

rr mtcars\(hpc <- mtcars\)hp/mtcars$cyl

Using mtcars: Weight is in thousands of pounds. Create a new variable that is weight in pounds.

rr mtcars\(wt_new <- mtcars\)wt*1000

Using mtcars: Create a new dataframe of cars that get at least 20 miles per gallon. How many cars is that?

rr new_mtcars <- mtcars[mtcars$mpg >= 20,]

Using mtcars: Create a new dataframe of cars that get fewer than 20 miles per gallon.

rr new_mtcars2 <- mtcars[mtcars$mpg < 20,]

PLOTS (histograms, plots, save pdf)

Make a plot of the number of participants at each event using the SCAD data. Label the x and y axis.

Save that plot

rr pdf(~/Desktop/UCLA/Courses/4_Fall 2019/PP 290/Assignments/assn1plot1.pdf) plot(data\(npart[data\)npart != -99], xlab = Index, ylab = of Participants) dev.off()

Make a histogram of the number of participants at each event. Make sure to label the axes.

Make a barplot of the number of participants per event. Hint: you will need to use table as well.

Load data, explore it.

Now plot it:

barplot(table(SCADdata$BYEAR))
no non-missing arguments to min; returning Infno non-missing arguments to max; returning -Infno non-missing arguments to min; returning Infno non-missing arguments to max; returning -InfError in plot.window(xlim, ylim, log = log, ...) : 
  need finite 'xlim' values

Using mcars: Make a table of the gear variable.

table(mtcars$gear)

 3  4  5 
15 12  5 

Using mcars: Make a proportion table of the gear variable.

prop.table(table(mtcars$gear))

      3       4       5 
0.46875 0.37500 0.15625 

Using mcars: Make a two-way table of the number of cylinders and number of gears.

table(mtcars$gear, mtcars$cyl)
   
     4  6  8
  3  1  2 12
  4  8  4  0
  5  2  1  2

Using mcars: Make a two-way proportion table of the number of cylinders and number of gears. Make one table where the cells add to 1, another where each column adds to 1, and another where each row does.

prop.table(table(mtcars$gear, mtcars$cyl), margin=1)
   
             4          6          8
  3 0.06666667 0.13333333 0.80000000
  4 0.66666667 0.33333333 0.00000000
  5 0.40000000 0.20000000 0.40000000

Using mcars: Plot the miles per gallon against the number of cylinders.

Using mcars: Make a barplot of the cars by the carb variable.

Using mcars: Make a histogram of miles per gallon.

#Non-Network Plots

names(mtcars)  # The variables
 [1] "mpg"  "cyl"  "disp" "hp"   "drat" "wt"   "qsec" "vs"   "am"   "gear" "carb"
[12] "car" 

Base Plotting

Base Plotting, with Multiple Variables: As we learned in lecture, a two-dimensional plot is often too simplistic for the relationships we want to inspect. We will now learn how to change the size, shape, and color of points.

Change the size with the cex argument

legend('topright', legend='Sized by displacement')
Error in strwidth(legend, units = "user", cex = cex, font = text.font) : 
  plot.new has not been called yet

Change the size with the cex argument

Why would a square root result in dots that are too large? mtcars is very large! Because you do not want the cex numbers to be too large, it is common to take the square root or logarithm. Here, square roots will still create dots that are too large, so I divide by 100 as well as take the logarithm.

What does sizing the dots by displacement reveal about the relationship of displacement with horsepower? What about with miles per gallon? decreased horsepower may be associated with increased miles per gallon.

Now, let us change the shape of points. The key is to use the pch argument, which stands for “point character”. If you look at the help documentation - ?par, then search for pch - you will see the various shapes base R has. The code below first shows you how to change the shape of each entry, and it then shows you how to create a variable enumerating the shape by some other variable.

Change the shape with the pch argument

Why are some dots circles but others triangles? the 1 and 2 in the pch line

Change the shape with the pch argument

This variable is convenient because it is already coded as a number

mtcars$am
 [1] 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 0 0 0 0 0 1 1 1 1 1 1 1

Change shape to be Mazda or not Mazda

Now, let us color the points. The approach is the same as we have seen - pass R a vector of colors and R will make the points those colors. Notice that I have double the size of each point with the cex argument; I do that simply to make the chart more legible.

Change the color with the col argument

What do you notice about the relationship between horsepower, miles per gallon, and the weight of a car?

Plot quarter mile second time against miles per gallon. Change the size based on the car’s weight.

Plot quarter mile second time against miles per gallon. Change the shape based on whether car has an automatic transmission.

Plot quarter mile second time against miles per gallon. Change the color based on whether the car has more than 4 cylinders.

Make one plot that combines the three plots above. That is, change the size, shape, and color of each point based on the car’s weight, transmission, and number of cylinders, respectively.

Modiying the size, shape, and color of nodes is identical. First, let us explore the karate club dataset.

NETWORK LAYOUT

Finally, let us look at how our networks look different depending on the layout we use. Frankly, igraph is very frustrating here, as the arguments to layout do not possess the same structure. If you want to try a different layout, I suggest googling.

Below is another way to generate coordinates. You generate the coordinates before calling plot, save that as an object, then pass that object to plot. This approach is what you would do if you want to manually change coordinates that igraph generates. You can also use this approach to generate your own layout.

CENTRALITY

Degree Centrality: Let us use a directed dataset, UKfaculty to explore different measures of degree centrality.

# The argument total, all is the same
degreeCentrality == dC2
 [1] TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE
[17] TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE
[33] TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE
[49] TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE
[65] TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE
[81] TRUE

Now, let us size nodes by their centrality:

It is often difficult to see how different centrality measures covary, since it is unclear what the exact number for each size is. It is therefore a good idea to visualize the network in other ways.

We will use the USairports data dataset, which represents flights in December 2010. Each edge is an airline route between two airports that uses the same aircraft type; there can be multiple edges between each node. You will notice that the edges have attributes as well.

Neighbor Cumulative Degree Centrality

In this section, we will calculate neighbor cumulative degree centrality. There is no default function in igraph to do this, so we will have to do it manually. We will make a directed network undirected and then loop over every node.

V(g)$ncdc == V(g)$ncdc2
 [1] TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE
[17] TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE
[33] TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE
[49] TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE
[65] TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE
[81] TRUE

Where do I create the vertex attribute for the NCDC measure? For each node, we have to get its neighbors and for each neighbor, get is degree. We then sum the degree of each neighbor and make that the vertex attribute so we can easily call on it for the NCDC measure.

Why do I reset total <- 0 for each new loop? We have to make the directed network undirected and then loop over every node.

Now, let us make plots.

Load the karate club data:

All Steps Centrality

In this section, we will examine the betweenness and eigenvector centrality for the karate club network. See page 95 of Luke 2015 for a list of the igraph centrality commands.

# Correlation of multiple variables
cor(data.frame(V(karate)$bc, V(karate)$ec, degree(karate)))
               V.karate..bc V.karate..ec degree.karate.
V.karate..bc      1.0000000    0.5180063      0.7882018
V.karate..ec      0.5180063    1.0000000      0.8783299
degree.karate.    0.7882018    0.8783299      1.0000000

Now, we will make plots:

Which nodes have high betweenness and eigenvector centrality?

Which nodes have low betweenness but high eigenvector centrality?

Which nodes have high betweeness but low eigenvector ventrality?

The following questions use the UKfaculty data. Pretend like you have not loaded it already. Note that you could make the graph undirected or you can tell each function to assume the graph is not directed:

LOOPS

Write a loop that prints the phrase “Hello World” ten times.

for (index in 1:10) { 
  print("Hello World")
}
[1] "Hello World"
[1] "Hello World"
[1] "Hello World"
[1] "Hello World"
[1] "Hello World"
[1] "Hello World"
[1] "Hello World"
[1] "Hello World"
[1] "Hello World"
[1] "Hello World"

Write a loop that prints the number of the loop multiplied by 10. For example, the first iteration prints 10, the second 20, and so on. Do this procedure for 20 iterations.

for (thisiteration in 1:20) {
print(thisiteration * 10) 
  }
[1] 10
[1] 20
[1] 30
[1] 40
[1] 50
[1] 60
[1] 70
[1] 80
[1] 90
[1] 100
[1] 110
[1] 120
[1] 130
[1] 140
[1] 150
[1] 160
[1] 170
[1] 180
[1] 190
[1] 200

for(i in 1:12){
  print('hello world')
  print(2*i)
}
[1] "hello world"
[1] 2
[1] "hello world"
[1] 4
[1] "hello world"
[1] 6
[1] "hello world"
[1] 8
[1] "hello world"
[1] 10
[1] "hello world"
[1] 12
[1] "hello world"
[1] 14
[1] "hello world"
[1] 16
[1] "hello world"
[1] 18
[1] "hello world"
[1] 20
[1] "hello world"
[1] 22
[1] "hello world"
[1] 24

How would you change the above loop to print a statement 20 times?

for(i in 1:20){  # For each of twenty iterations
  print('Hello world')  # print the phrase, "Hello world"
  print(2*i)
} 
[1] "Hello world"
[1] 2
[1] "Hello world"
[1] 4
[1] "Hello world"
[1] 6
[1] "Hello world"
[1] 8
[1] "Hello world"
[1] 10
[1] "Hello world"
[1] 12
[1] "Hello world"
[1] 14
[1] "Hello world"
[1] 16
[1] "Hello world"
[1] 18
[1] "Hello world"
[1] 20
[1] "Hello world"
[1] 22
[1] "Hello world"
[1] 24
[1] "Hello world"
[1] 26
[1] "Hello world"
[1] 28
[1] "Hello world"
[1] 30
[1] "Hello world"
[1] 32
[1] "Hello world"
[1] 34
[1] "Hello world"
[1] 36
[1] "Hello world"
[1] 38
[1] "Hello world"
[1] 40

Vectors are faster loops. If you are going to create a vector during a loop, it needs to be defined before the loop.

for(i in 1:length(two)){  # Notice how I dynamically changed
  print(result[i] <- one[i]*two[i])
}
[1] 10
[1] 100
[1] 60
[1] 162
[1] 1000
[1] 300

i is used to index place by convention, but you can use anything:

for(waytoolongindex in 1:5){
  print(waytoolongindex)
}
[1] 1
[1] 2
[1] 3
[1] 4
[1] 5

You could even use it to make a lot of graphs. Do not forget to change the filepath to be your computer.

d <- read.csv("https://stats.idre.ucla.edu/stat/data/hsbraw.csv", stringsAsFactors = FALSE)

ycolumns <- c('write', 'math', 'science', 'socst')


for(i in 1:length(ycolumns)){
  filename <- paste0('~/Desktop/loopplotexample_', ycolumns[i],'.pdf')
  pdf(filename)
  plot(d$read, d[[ycolumns[i]]], pch=20, xlab='Reading Score', ylab=ycolumns[i])
  dev.off()
}

q()

It is hard to keep each value straight, however. And if we want to return to these results later, we need to rerun the loop. Doing so is annoying and will create slightly different answers because of the random number generator. Let us learn how to save the data as they are created.

In the loop, I show two ways to create the data. The commented one, with # in front of it, does not run but may be easier to read. To use it, remove the # from each line and put # in front of lines that are currently executed. To add or remove comments to multiple lines, highlight the ones you want to change and type cmd+c; on a Windows machine, you probably will replace cmd with the flag key.

Use a loop and igraph to create random networks. Vary the probability of forming an edge 10 times. Put some resulting statistics in a dataframe.

remember2 <- data.frame(iteration=NULL, tieProbability=NULL, avgDegree=NULL, globalClustering=NULL, glbalClustering2=NULL, density=NULL)

theps <- c(.01, .1, .2, .3, .4, .5, .6, .7, .8, .9)
iterations <- length(theps)

for(i in 1:iterations){
  temp <- erdos.renyi.game(howmanyrows, p=randomness[i])
  
  # iteration <- i
  # tieProbability <- randomness[i]
  # avgDegree <- mean(degree(temp))
  # globalClustering <- transitivity(temp, type='global')
  # globalClustering2 <- mean(transitivity(temp, type='local'))
  # density <- graph.density(temp)
  # 
  d <- data.frame(iteration=i, tieProbability=randomness[i], avgDegree=mean(degree(temp)), globalClustering=transitivity(temp, type='global'), globalClustering2=mean(transitivity(temp, type='local')), density=graph.density(temp))
  
  # d <- data.frame(iteration=iteration, tieProbability=tieProbability, avgDegree=avgDegree, globalClustering=globalClustering, globalClustering2=globalClustering2, density=density)
  remember2 <- rbind(remember2, d)  # "rowbind", adds a new row, d, to whatever exists in remember.
  #print(remember)
}

remember2

nrow(remember2)
nrow(remember)

Vary parameters using a loop:

randomness <- c(.01, .1, .3, .5, .7, .9, 1)
iterations <- length(randomness)  # Why did I define iterations this way?
howmanyrows <- 100

# Will not show anything
for(i in 1:iterations){
  temp <- erdos.renyi.game(howmanyrows, p=randomness[i])
  summary(degree(temp))
  transitivity(temp, type='global')
  mean(transitivity(temp, type='local'))
  graph.density(temp)
}

# Need to print
for(i in 1:iterations){
  temp <- erdos.renyi.game(howmanyrows, p=randomness[i])
  print(paste0('On trial ', i))  # This line lets me know which iteration I am on
  print('Summary Statistics:')
  print(summary(degree(temp)))
  print(paste0('Global clustering is: ', transitivity(temp, type='global')))
  print('Average of the each node\'s clustering is: ')  # Maybe you find it easier to read the output this way
  print(mean(transitivity(temp, type='local'), na.rm=TRUE))
  print(paste0('The graph\'s density is: ', graph.density(temp)))
}

KEYWORDS

  1. Script - The text document that contains the commands. It can be in any format, though usually you will see .txt or .R.

  2. Console - Where you see the code run when it is executed. In R Studio, the console is the top right window by default.

  3. Vector - The basic building block of R, a vector is a sequence of items that you will manipulate. A vector can contain as few as one item

  4. Object - R is what is known as an “object-oriented language”. This means there are abstract entitites - the objects - that contain the data on which you work. An entity could be a dataset, a list of numbers, a database, or anything else.

  5. Dataframe - The primary type of object used to represent data in R. Think of a dataframe as a spreadsheet: it has rows and columns, where each row represents a unit of analysis with records for each of the columns (the measured variables). A dataframe is a collection of vectors. A dataframe is a matrix whose vectors can be different data types, such as strings and numbers.

  6. String - If something is a “string”, that means it is a word. A string is in contrast to numeric data.

  7. Package - A set of code that extends the functionality of base R. Each package will have a different purpose, and the clarity of the documentation, as well as functionality of the code, is at the whim of the package’s creator. All packages are free to use.

  8. Debug, Refactor - These two words are fancy ways of saying you are finding and correcting errors in your code.

  9. List - A list is a collection of vectors. Whereas a vector must contain data of the same type, a list can contain vectors of different types (each vector still must only contain one data type).

  10. Matrix - A matrix is a series of vectors of numeric data. A matrix has to contain numeric data. They are important because they can represent relationships in a network, so constructing and manipulating them is important for network analysis.Since we now have an object with two dimensions (rows and columns), we access them differently than we did vectors. A whole row is object[rownumber,], a whole column is object[,columnumber], and a specific cell is object[row, column].

  11. tidyverse - is actually a series of packages created by Hadley Wickham, the two most common of which are dplyr for data merging and aggregation and ggplot for generating graphics. To keep from overloading you with information, I will not actually use the tidyverse in this class, but it is quite common in the R world. igraph is the network package that we will use throughout the quarter.

In addition to using code, you can also use a graphical interface to install packages. In base R, click Packages & Data, then Package Installer. You can then search for the package by name. In R Studio, navigate to Tools, then Install Packages, and search for the package by name. Below’s code shows how to do this process in your script.1

  1. CSV - The main kind of data you will read is .csv, which stands for comma-separated values. It is a text file where each column is separated by a comma. Make sure you change the file path to be to where your data are. Note that read read. line works; the point is that read.csv is a special case of read.delim. This distinction matters because you could have data that are separated by a feature that is not a comma, like a tab or |.

  2. Loops - You use loops when you want to repeat a piece of code multiple times. For example, later in the course we will run network simulations where we want to see how an outcome diffuses over time; each unit of time will correspond to one iteration of the loop. Or you may have a list of files to read, so you will loop over that list in order to load them one at a time. While it is best to use vector operations whenever possible, loops can be more intuitive.


  1. If you look at the R Markdown file, you will notice the option “eval=FALSE”, whereas the other code chunks use “eval=TRUE”. eval tells R Markdown whether or not to execute the code in that chunk. Since I do not want to install these packages every time I compile the document, I set it to FALSE here.

LS0tCnRpdGxlOiBNaWR0ZXJtIFByZXAKb3V0cHV0OiBodG1sX25vdGVib29rCi0tLQoKCgoKCgoKCgojIyMjIyBCQVNJQ1MgCgoKVHlwZSAncSgpJyB0byBxdWl0IFIuCgpBc2tpbmcgZm9yIGhlbHA6CmBgYHtyfQojIFdoZW4geW91IGtub3cgdGhlIGZ1bmN0aW9uIG5hbWUsIHVzZSBvbmUgcXVlc3Rpb24gbWFyay4KP3RhaWwKCiMgV2hlbiB5b3Ugc29ydCBvZiBrbm93LCB1c2UgdHdvIHF1ZXN0aW9uIG1hcmtzLgo/P2F2ZXJhZ2UKPz90YWlsCmBgYAoKSW5zdGFsbCBpZ3JhcGgsIGhhdmVuLCBhbmQgdGlkeXZlcnNlOgpgYGB7cn0KaW5zdGFsbC5wYWNrYWdlcygndGlkeXZlcnNlJykKCmluc3RhbGwucGFja2FnZXMoJ2lncmFwaCcpCgppbnN0YWxsLnBhY2thZ2VzKCJoYXZlbiIpCmBgYAoKU2V0IHNlZWQ6CmBgYHtyfQpzZXQuc2VlZCgwOTAzMjAxOSkKYGBgCgpMb2FkIGlncmFwaCAsIGhhdmVuLCBhbmQgdGlkeXZlcnNlIE9SIGF0dGFjaCBkYXRhIGFscmVhZHkgbG9hZGVkIGluIFI6CmBgYHtyfQpsaWJyYXJ5KGlncmFwaCkKCmxpYnJhcnkodGlkeXZlcnNlKQoKbGlicmFyeShoYXZlbikKCmxpYnJhcnkocmVhZHhsKSAKCmF0dGFjaChtdGNhcnMpCmBgYAoKUmVhZCBpbiBkYXRhIGZyb20gYSBkZXNrdG9wOgpgYGB7cn0KZGF0YTIgPC0gcmVhZC5kZWxpbSgnfi9Eb2N1bWVudHMvVUNMQS9EYXRhL1BSSU9fVXJiYW5Tb2NpYWxEaXNvcmRlci9ldmVudHMuY3N2Jywgc2VwPScsJykKCmRhdGEgPC0gcmVhZC5jc3YoJ34vRG9jdW1lbnRzL1VDTEEvRGF0YS9QUklPX1VyYmFuU29jaWFsRGlzb3JkZXIvZXZlbnRzLmNzdicpCmBgYAoKUmVhZCBpbiBkYXRhIGZyb20gYSB3ZWJzaXRlOgpgYGB7cn0KZGF0X2NzdiA8LSByZWFkLmNzdigiaHR0cHM6Ly9zdGF0cy5pZHJlLnVjbGEuZWR1L3N0YXQvZGF0YS9oc2JkZW1vLmNzdiIpCgpkYXRfdGFiIDwtIHJlYWQuZGVsaW0oImh0dHBzOi8vc3RhdHMuaWRyZS51Y2xhLmVkdS9zdGF0L2RhdGEvaHNiMi50eHQiLCBzZXA9Ilx0IikKCiMgWW91IGNhbiBhbHNvIHJlYWQgcHJvcHJpZXRhcnkgZGF0YSBmb3JtYXRzIChgLnhsc2AsIGAuZHRhYCwgYW5kIHNvIG9uKSB1c2luZyB2YXJpb3VzIFIgcGFja2FnZXMuICBUaGUgbW9zdCBwb3dlcmZ1bCBpcyBgaGF2ZW5gLiAKCiMgU1BTUwpkYXRfc3BzcyA8LSByZWFkX3Nwc3MoImh0dHBzOi8vc3RhdHMuaWRyZS51Y2xhLmVkdS9zdGF0L2RhdGEvaHNiMi5zYXYiKQoKIyBTdGF0YQpkYXRfZHRhIDwtIHJlYWRfc3RhdGEoImh0dHBzOi8vc3RhdHMuaWRyZS51Y2xhLmVkdS9zdGF0L2RhdGEvaHNiZGVtby5kdGEiKQoKIyBUaGlzIHN0ZXAgb25seSBuZWVkZWQgdG8gcmVhZCBleGNlbCBmaWxlcyBmcm9tIHRoZSBpbnRlcm5ldDoKZG93bmxvYWQuZmlsZSgiaHR0cHM6Ly9zdGF0cy5pZHJlLnVjbGEuZWR1L3N0YXQvZGF0YS9oc2IyLnhscyIsICJ+L0Rvd25sb2Fkcy9teWZpbGUueGxzIiwgbW9kZT0id2IiKQoKZGF0X3hscyA8LSByZWFkX2V4Y2VsKCJ+L0Rvd25sb2Fkcy9teWZpbGUueGxzIikKYGBgCgpVc2UgYHJlYWRfZ3JhcGhgIHRvIGxvYWQgbmV0d29yayBkYXRhLiBDcmVhdGUgYSBuZXR3b3JrIGZyb20gYW4gZWRnZWxpc3QuIExvYWQgZGF0YSBhdCBodHRwczovL3Jhdy5naXRodWJ1c2VyY29udGVudC5jb20vWmFjaGFyeVNUL0RhdGEvbWFzdGVyL2RvbHBoaW5zLiBPcGVuIHRoYXQgbGluayBmaXJzdCBpbiB5b3VyIHdlYiBicm93c2VyIHNvIHlvdSBrbm93IHdoYXQgdGhlIGRhdGEgbG9vayBsaWtlLiBUaGUgZGF0YSBhcmUgdW5kaXJlY3RlZCAodHdvIG9wdGlvbnMpOgpgYGB7cn0KZGF0YXBzMiA8LSByZWFkX2dyYXBoKCdodHRwczovL3Jhdy5naXRodWJ1c2VyY29udGVudC5jb20vWmFjaGFyeVNUL0RhdGEvbWFzdGVyL2RvbHBoaW5zJywgZGlyZWN0ZWQ9RkFMU0UsIGZvcm1hdCA9IGMoImVkZ2VsaXN0IikpCgpkYXRhIDwtIHJlYWQudGFibGUoImh0dHBzOi8vcmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbS9aYWNoYXJ5U1QvRGF0YS9tYXN0ZXIvZG9scGhpbnMiLApoZWFkZXIgPSBGQUxTRSkKZGF0YSA8LSBncmFwaF9mcm9tX2VkZ2VsaXN0KGFzLm1hdHJpeChkYXRhKSwgZGlyZWN0ZWQgPSBGQUxTRSkKYGBgCgpHZXQgdGhlIGFkamFjZW5jeSBtYXRyaXggZnJvbSB0aGUgZ3JhcGggYWJvdmUuIFRlbGwgdGhlIGZ1bmN0aW9uIG5vdCB0byByZXR1cm4gYSBzcGFyc2UgbWF0cml4LiAKYGBge3J9CmFtcHMyIDwtIGFzX2FkamFjZW5jeV9tYXRyaXgoZGF0YXBzMiwgc3BhcnNlID0gRkFMU0UpCmBgYAoKTG9hZCB0aGUgZG9scGhpbiBkYXRhIGFnYWluLCB0aGlzIHRpbWUgdGVsbGluZyBSIHRoZSBuZXR3b3JrIGlzIGRpcmVjdGVkLgpgYGB7cn0KZGF0YXBzMmQgPC0gcmVhZF9ncmFwaCgiaHR0cHM6Ly9yYXcuZ2l0aHVidXNlcmNvbnRlbnQuY29tL1phY2hhcnlTVC9EYXRhL21hc3Rlci9kb2xwaGlucyIsIGRpcmVjdGVkID0gVFJVRSkgCmBgYAoKTG9hZCB0aGUgU0NBRCBkYXRhIGZyb20gbXkgR2l0aHViIERhdGEgcmVwb3NpdG9yeS4gVGhlIGxpbmsgaXMgaHR0cHM6Ly9yYXcuZ2l0aHVidXNlcmNvbnRlbnQuY29tL1phY2hhcnlTVC9EYXRhL21hc3Rlci9zY2FkX2xhdGluYW1lIHJpY2EuCmBgYHtyfQpTQ0FEZGF0YSA8LSByZWFkLmNzdigiaHR0cHM6Ly9yYXcuZ2l0aHVidXNlcmNvbnRlbnQuY29tL1phY2hhcnlTVC9EYXRhL21hc3Rlci9zY2FkX2xhdGluYW1lcmljYSIpCmBgYAoKCgoKCgoKCiAgCgoKCiMjIyMjIElOU1BFQ1RJTkcgQSBORVRXT1JLIChlZGdlcywgbm9kZXMsIGNvbXBvbmVudHMsIHNpemUsIGRlZ3JlZSkKCgpIb3cgbWFueSBub2RlcyBkb2VzIHRoZSBuZXR3b3JrIGhhdmUgKHR3byBvcHRpb25zKT8KYGBge3J9CnZjb3VudChkYXRhcHMyKQpsZW5ndGgoVihkYXRhcHMyKSkKYGBgCgpIb3cgbWFueSBub2RlcyBkb2VzIHRoZSBuZXR3b3JrIGhhdmUgKHR3byBvcHRpb25zKSB1c2luZyBhbiBhZGphY2VuY3kgbWF0cml4PwpgYGB7cn0KbnJvdyhhbXBzMikKbmNvbChhbXBzMikKYGBgCgpIb3cgbWFueSBlZGdlcyBkb2VzIHRoZSBuZXR3b3JrIGhhdmUgKHRocmVlIG9wdGlvbnMpPwpgYGB7cn0KZWNvdW50KGRhdGFwczIpCnN1bShyb3dTdW1zKGFtcHMyKSkvMiAKc3VtKGNvbFN1bXMoYW1wczIpKS8yCmBgYAoKRG9lcyB0aGUgbmV0d29yayBoYXZlIGFueSBjb21wb25lbnRzPwpgYGB7cn0KY29tcG9uZW50cyhkYXRhcHMyKQpjb3VudF9jb21wb25lbnRzKGRhdGFwczIpCmBgYAoKU2F2ZSB0aGUgcmVzdWx0cyBmcm9tIGFib3ZlIGFzIGEgbmV3IG9iamVjdC4gQ2FsbCB0aGUgZW50cnkgb2YgdGhhdCBvYmplY3QgdGhhdCB0ZWxscyB5b3UgaGF2ZSBtYW55IGNvbXBvbmVudHMgdGhlcmUgYXJlLgpgYGB7cn0KYyA8LSBjb21wb25lbnRzKGRhdGFwczIpCmMkbm8KYGBgCgpXaGF0IGlzIHRoZSBzaXplIG9mIHRoZSBjb21wb25lbnQocyk/CmBgYHtyfQpjJGNzaXplCmBgYAoKV2hhdCBpcyB0aGUgZGVncmVlIG9mIGVhY2ggbm9kZT8gIFVzZSB0aGUgb2JqZWN0IGNyZWF0ZWQgd2hlbiB5b3UgcmVhZCBpbiB0aGUgZmlsZS4KYGBge3J9CmRlZ3JlZShkYXRhcHMyKQpgYGAKCldoYXQgaXMgdGhlIGRlZ3JlZSBvZiBlYWNoIG5vZGU/ICBVc2UgdGhlIGFkamFjZW5jeSBtYXRyaXg/CmBgYHtyfQpyb3dTdW1zKGFtcHMyKQpgYGAKCldoYXQgaXMgdGhlIGRlZ3JlZSBvZiB0aGUgbW9zdCBzb2NpYWwgZG9scGhpbj8KYGBge3J9Cm1heChkZWdyZWUoZGF0YXBzMikpCmBgYAoKV2hpY2ggZG9scGhpbiBpcyB0aGUgbW9zdCBzb2NpYWw/CmBgYHtyfQpWKGRhdGFwczIpW2RlZ3JlZShkYXRhcHMyKSA9PSBtYXgoZGVncmVlKGRhdGFwczIpKV0KYGBgCgpSZXBlYXQgdGhlIGxhc3QgdHdvIHF1ZXJpZXMgYWJvdmUsIGJ1dCBzYXZlIHRoZSByZXN1bHQgb2YgdGhlIG1vc3Qgc29jaWFsIGRvbHBoaW4gYXMgYW4gb2JqZWN0LgpgYGB7cn0Kc29jaWFsIDwtIG1heChkZWdyZWUoZGF0YXBzMikpIApWKGRhdGFwczIpW2RlZ3JlZShkYXRhcHMyKSA9PSBzb2NpYWxdCmBgYAoKTG9hZCBhbmQgaW5zcGVjdCB0aGUgS0FSQVRFIGRhdGEuIApgYGB7cn0KZCA8LSByZWFkLnRhYmxlKCdodHRwczovL3Jhdy5naXRodWJ1c2VyY29udGVudC5jb20vWmFjaGFyeVNUL0RhdGEvbWFzdGVyL3phY2hhcnlfa2FyYXRlJywgaGVhZGVyPUZBTFNFKQpkaW0oZCkgIApoZWFkKGQpICAjIEZpcnN0IHNpeCByb3dzIGxvb2sgZmluZSwgdGhvdWdoIEkgZG8gbm90IGxpa2UgdGhlIGNvbHVtbiBuYW1lcy4KYGBgCgpNYWtlIGNvbHVtbiBuYW1lcyBtYXRjaCB0aGUgcm93IG51bWJlcnM6CmBgYHtyfQpuYW1lcyhkKSA8LSBhcy5jaGFyYWN0ZXIoMTpucm93KGQpKSAgCmhlYWQoZCkKIyBGb3IgdGhlIG51bWJlcnMgMSB0byBob3dldmVyIG1hbnkgcm93cyB0aGVyZSBhcmUsIG1ha2UgdGhhdCB0aGUgY29sdW1uIG5hbWUuICBNdXN0IGJlIGEgc3RyaW5nLgpgYGAKCk5vdywgbGV0IHVzIGNvdW50IGhvdyBtYW55IG5vZGVzIHRoZXJlIGFyZSwgdGhlIG51bWJlciBvZiBjb21wb25lbnRzLCBhbmQgdGhlIGRlZ3JlZSBvZiBlYWNoIG5vZGU6CmBgYHtyfQpsaWJyYXJ5KGlncmFwaCkgICMgVG8gbG9hZCBpZ3JhcGggZnVuY3Rpb25zCmcgPC0gZ3JhcGgoZCkgICMgRmFpbHMuICBSZXF1aXJlcyBlZGdlbGlzdC4KZyA8LSBncmFwaF9mcm9tX2FkamFjZW5jeV9tYXRyaXgoZCkgICMgRmFpbHMgYmVjYXVzZSBkIGlzIGEgZGF0YWZyYW1lLgpnIDwtIGdyYXBoX2Zyb21fYWRqYWNlbmN5X21hdHJpeChhcy5tYXRyaXgoZCksIG1vZGU9InVuZGlyZWN0ZWQiKSAgIyBXb3Jrcy4gIERvIG5vdCBmb3JnZXQgdG8gdGVsbCBpdCB0aGUga2luZCBvZiBuZXR3b3JrIHlvdSBoYXZlLgpgYGAKClRvIGp1c3QgZ2V0IHRoZSBjb3VudCBvZiBjb21wb25lbnRzOgpgYGB7cn0KY291bnRfY29tcG9uZW50cyhnKQpgYGAKClRoZSBtb3N0IGNvbm5lY3Rpb25zIGFuZCBpdHMgbm9kZSBvciBub2RlcyAodHdvIG9wdGlvbnMpOgpgYGB7cn0KbWF4KGRlZ3JlZShnKSkKVihnKVtkZWdyZWUoZykgPT0gbWF4KGRlZ3JlZShnKSldCgptYXhpbXVtIDwtIG1heChkZWdyZWUoZykpCm1heGltdW0KVihnKVtkZWdyZWUoZykgPT0gbWF4aW11bV0KYGBgCgpMb29wLCBjaGVjayB3b3JrLCBjb3JyZWN0OgpgYGB7cn0KZGVncmVlcyA8LSBOVUxMCmZvcihpIGluIDE6bmNvbChkKSl7CiAgZGVncmVlc1tpXSA8LSBzdW0oZFssaV0pCn0KCiMgVXNpbmcgdGhlIGFwcGx5IGZ1bmN0aW9uOiAgCiMgTUFSR0lOPTIgbWVhbnMgY29sdW1ucy4KYXBwbHkoZCwgTUFSR0lOPTIsIEZVTj1zdW0pICAjIFlvdSBoYXZlIHRvIGNhcGl0YWxpemUgdGhlIGFyZ3VtZW50IG5hbWVzLgpgYGAKCkZpbmQgdGhlIG5vZGVzIGluIHRoZSBrYXJhdGUgbmV0d29yayB0aGF0IGhhdmUgdGhlIG1pbmltdW0gZGVncmVlLgpgYGB7cn0KdGhlbWluIDwtIG1pbihkZWdyZWUoZykpCnRoZW1pbgpWKGcpW2RlZ3JlZShnKSA9PSB0aGVtaW5dCgpWKGcpW3doaWNoLm1pbihkZWdyZWUoZykpXQpgYGAKCgoKCgoKCgoKCgoKIyMjIyBSQU5ET00gTlVNQkVSIEdFTkVSQVRPUiAoaWdyYXBoLCBhZGphY2VuY3kgbWF0cml4KQoKCmBgYHtyfQpzZXQuc2VlZCgwMTA4MjAxNykKYGBgCgpBIGJpbm9taWFsIGRpc3RyaWJ1dGlvbiBpcyBvbmUgdGhhdCBwcm9kdWNlcyBhIHNlcmllcyBvZiBudW1iZXJzIGFjY29yZGluZyB0byBwYXJhbWV0ZXJzIHlvdSBwYXNzIGl0LldlIGNhbiBlYXNpbHkgbWFrZSBpdCBwcm9kdWNlcyAxcyBhbmQgMHMgYW5kIHRoZW4gcG9wdWxhdGUgYW4gYWRqYWNlbmN5IG1hdHJpeCB3aXRoIHRoZW0uIFRoZSBsYXN0IGFyZ3VtZW50IGNvbnRyb2xzIHRoZSByYXRpbyBvZiAxcyBhbmQgMHMuICBTbywgaGFsZiB0aGUgb3V0cHV0IHdpbGwgYmUgMSwgaGFsZiB3aWxsIGJlIDAsIG9uIGF2ZXJhZ2UuCmBgYHtyfQpyYmlub20oMSwxLC41KQpyYmlub20oMSwxLC41KQpyYmlub20oMSwxLC41KQpyYmlub20oMSwxLC41KQpyYmlub20oMSwxLC41KQpgYGAKCkxldCdzIHNwZWVkIHRoYXQgdXAuICBXaHkgZG9lcyB0aGUgYmVsb3cgbGluZSBwcm9kdWNlIG1vcmUgbnVtYmVycz8gMTAgcmVwbGFjZXMgMS4KYGBge3J9CnJiaW5vbSgxMCwxLC41KQpgYGAKCkxldCdzIHVuZGVyc3RhbmQgcmJpbm9tIGEgbGl0dGxlIG1vcmUuIFNlY29uZCBudW1iZXIgaXMgb3B0aW9ucy4gCmBgYHtyfQpyYmlub20oMTAsMTAsLjUpCnJiaW5vbSgxMCwzLC41KQpgYGAKCldlIGNhbiBub3cgbWFrZSBhIG1hdHJpeCBvZiBhcmJpdHJhcnkgc2l6ZSwgc28gbG9uZyBhcyByYmlub20oKSBjcmVhdGVzIGEgc3F1YXJlIGFtb3VudCBvZiBudW1iZXJzLgpgYGB7cn0KdGVtcCA8LSBtYXRyaXgocmJpbm9tKDgxLCAxLCAuNSksIG5yb3c9OSkKdGVtcApgYGAKCk1heWJlIHRoaXMgYXBwcm9hY2ggaXMgYSBsaXR0bGUgbW9yZSBpbnR1aXRpdmU6CmBgYHtyfQpob3dtYW55cm93cyA8LSAxNQp0ZW1wIDwtIG1hdHJpeChyYmlub20oaG93bWFueXJvd3NeMiwgMSwgLjUpLCBucm93PWhvd21hbnlyb3dzKQpkaWFnKHRlbXApIDwtIDAgICMgTWFrZSBzdXJlIHRoZXJlIGFyZSBubyBzZWxmLWxvb3BzLgp0ZW1wCmBgYAoKQ29udmVydCB0byBpZ3JhcGggb2JqZWN0LgpgYGB7cn0KZ190ZW1wIDwtIGdyYXBoX2Zyb21fYWRqYWNlbmN5X21hdHJpeCh0ZW1wLCBtb2RlPSJkaXJlY3RlZCIpICAKYGBgCgpOb3RlIHRoYXQgdGhpcyBhcHByb2FjaCByZXF1aXJlcyB1c2luZyBhIGRpcmVjdGVkIG5ldHdvcmsuCmBgYHtyfQpjb2xTdW1zKHRlbXApICsgcm93U3Vtcyh0ZW1wKQpkZWdyZWUoZ190ZW1wKQooY29sU3Vtcyh0ZW1wKSArIHJvd1N1bXModGVtcCkpID09IGRlZ3JlZShnX3RlbXApCmBgYAoKWW91IGNhbiBhbHNvIG1ha2UgYW4gdW5kaXJlY3RlZCBncmFwaCBmcm9tIHRoZSBhZGphY2VuY3kgbWF0cml4LiBpZ3JhcGggd2lsbCBoYW5kbGUgdGhlIGNvbnN0cnVjdGlvbiBvZiBlZGdlcyBieSBhc3N1bWluZyBhbnkgZWRnZSBpcyBzeW1tZXRyaWMuCmBgYHtyfQpnX3RlbXAyIDwtIGdyYXBoX2Zyb21fYWRqYWNlbmN5X21hdHJpeCh0ZW1wLCBtb2RlPSJ1bmRpcmVjdGVkIikgIAoKY29sU3Vtcyh0ZW1wKSArIHJvd1N1bXModGVtcCkgLSBjb2xTdW1zKHRlbXAqdCh0ZW1wKSkgICMgVGhlIGxhc3QgcGFydCBnaXZlcyB5b3UgdGhlIG51bWJlciBvZiBiaWRpcmVjdGlvbmFsIGNvbm5lY3Rpb25zLgpkZWdyZWUoZ190ZW1wMikgIyBHaXZlcyB0aGUgc2FtZSBhcyB0aGUgYWJvdmUgZm9yIGdyYXBoCmBgYAoKQnV0IGNhbGN1bGF0aW9uIG9mIG90aGVyIHN0YXRpc3RpY3Mgd2lsbCBiZSB0cmlja3kuICAqKkl0IGlzIGJldHRlciB0byBqdXN0IG1ha2UgdGhlIG1hdHJpeCBzeW1tZXRyaWMgcmlnaHQgYXdheSoqLCB3aGljaCByZXF1aXJlcyBzb21lIG1hc3NhZ2luZy4gRm9yIHRoZSBib3R0b20gbGVmdCBoYWxmIG9mIHRoZSBtYXRyaXgsIG1ha2UgaXQgdGhlIHNhbWUgYXMgdGhlIHRvcCByaWdodC4KYGBge3J9CnRlbXAyIDwtIHRlbXAKdGVtcDJbbG93ZXIudHJpKHRlbXAyKV0gPC0gdCh0ZW1wMilbbG93ZXIudHJpKHRlbXAyKV0KdGVtcDIKYGBgCgoKCgoKCgoKCgoKCgoKIyMjIyBBREpBQ0VOQ1kgTUFUUklYIEVYQU1QTEUKCgpDcmVhdGUgYW4gYWRqYWNlbmN5IG1hdHJpeCB0aGF0IGhhcyAyNSByb3dzIGFuZCBjb2x1bW5zLCB3aGVyZSBlYWNoIGNlbGwgaGFzIGEgLjggcHJvYmFiaWxpdHkgb2YgY29udGFpbmluZyBhIDEuICBTZXQgdGhlIGRpYWdvbmFsIHRvIHplcm8uCmBgYHtyfQpwcm9iYWJpbGl0eSA8LSAuOAp0aGlzbnVtYmVyIDwtIDI1CnRoaXNtYXRyaXggPC0gbWF0cml4KHJiaW5vbSh0aGlzbnVtYmVyXjIsIDEsIHByb2JhYmlsaXR5KSwgbnJvdz10aGlzbnVtYmVyKQpkaWFnKHRoaXNtYXRyaXgpIDwtIDAKdGhpc21hdHJpeApgYGAKCkNvbnZlcnQgaXQgdG8gYW4gdW5kaXJlY3RlZCBuZXR3b3JrLgpgYGB7cn0KdGhpc21hdHJpeFtsb3dlci50cmkodGhpc21hdHJpeCldIDwtIHQodGhpc21hdHJpeClbbG93ZXIudHJpKHRoaXNtYXRyaXgpXQp0aGlzbWF0cml4CmBgYAoKQ29udmVydCB0aGUgbWF0cml4IHRvIGFuIGlncmFwaCBncmFwaCBvYmplY3QuCmBgYHtyfQppZzIgPC0gZ3JhcGhfZnJvbV9hZGphY2VuY3lfbWF0cml4KHRoaXNtYXRyaXgsIG1vZGU9J3VuZGlyZWN0ZWQnKQpgYGAKCldoYXQgaXMgdGhlIGF2ZXJhZ2UgZGVncmVlIG9mIHRoZSBuZXR3b3JrPwpgYGB7cn0KbWVhbihkZWdyZWUoaWcyKSkKc3VtbWFyeShkZWdyZWUoaWcyKSkKc3VtbWFyeShkZWdyZWUoaWcyKSlbNF0KYGBgCgpXaGF0IGlzIHRoZSBkZW5zaXR5IG9mIHRoZSBuZXR3b3JrPwpgYGB7cn0KZGVuc2l0eShpZzIpICMgZmFpbHMKZ3JhcGguZGVuc2l0eShpZzIpICAjIHdvcmtzCmVkZ2VfZGVuc2l0eShpZzIpICAjIHNhbWUKYGBgCgpXaGF0IGlzIHRoZSBnbG9iYWwgdHJhbnNpdGl2aXR5IG9mIHRoZSBuZXR3b3JrPwpgYGB7cn0KdHJhbnNpdGl2aXR5KGlnMiwgdHlwZT0nZ2xvYmFsdW5kaXJlY3RlZCcpCmBgYAoKVXNlIGFuIGFkamFjZW5jeSBtYXRyaXggdG8gY3JlYXRlIGEgZmFrZSBuZXR3b3JrIHRoYXQgaGFzIDMwIGluZGl2aWR1YWxzLiAKYGBge3J9CnBlb3BsZSA8LSAzMApuZXdtYXRyaXggPC0gbWF0cml4KHJiaW5vbShwZW9wbGVeMiwgMSwgLjUpLCBucm93PXBlb3BsZSkKbmV3bWF0cml4CmBgYAoKQ2FsY3VsYXRlIHRoZSBkZWdyZWUgb2YgZWFjaCBub2RlIHVzaW5nIHRoZSBhZGphY2VuY3kgbWF0cml4OgpgYGB7cn0KZGVncmVlKG5ld21hdHJpeCkgICMgZmFpbHMgc2luY2Ugbm90IGdyYXBoIG9iamVjdApgYGAKCkRlZ3JlZSBmb3IgZGlyZWN0ZWQgYWRqYWNlbmN5IG1hdHJpeDoKYGBge3J9CmNvbFN1bXMobmV3bWF0cml4KSArIHJvd1N1bXMobmV3bWF0cml4KQpjb2xTdW1zKG5ld21hdHJpeCkgKyByb3dTdW1zKG5ld21hdHJpeCkgLSAyKmRpYWcobmV3bWF0cml4KSAjIE5vIHNlbGYgbG9vcHMKYGBgCgpEZWdyZWUgZm9yIHVuZGlyZWN0ZWQKYGBge3J9CmNvbFN1bXMobmV3bWF0cml4KSArIHJvd1N1bXMobmV3bWF0cml4KSAtIGRpYWcobmV3bWF0cml4KSAjIE5vIHNlbGYgbG9vcHMKYGBgCgpDb252ZXJ0IHRoZSBhZGphY2VuY3kgbWF0cml4IHRvIGFuIGFzeW1tZXRyaWMgZ3JhcGggb2JqZWN0LiAgVXNlIGlncmFwaCB0byBjYWxjdWxhdGUgdGhlIGRlZ3JlZSBvZiBlYWNoIG5vZGUuCmBgYHtyfQpnMyA8LSBncmFwaF9mcm9tX2FkamFjZW5jeV9tYXRyaXgobmV3bWF0cml4LCBtb2RlPSdkaXJlY3RlZCcpCmRlZ3JlZShnMykKYGBgCgoKCgoKCgoKCgoKCgoKIyMjIyMgREVOU0lUWQoKCldoYXQgaXMgdGhlIGRlbnNpdHkgb2YgdGhlIGRvbHBoaW4gbmV0d29yayAodHdvIG9wdGlvbnMpPwpgYGB7cn0KZ3JhcGguZGVuc2l0eShkYXRhcHMyKQplY291bnQoZGF0YXBzMikvKCh2Y291bnQoZGF0YXBzMikgKiAodmNvdW50KGRhdGFwczIpIC0gMSkpLzIpCmBgYAoKV2hhdCBpcyB0aGUgZGVuc2l0eSBvZiB0aGUgZGlyZWN0ZWQgZG9scGhpbiBkYXRhPwpgYGB7cn0KZ3JhcGguZGVuc2l0eShkYXRhcHMyZCkKYGBgCgpXaHkgaXMgdGhlIGRlbnNpdHkgdGhpcyB2YWx1ZSBub3c/ICBIb3cgZG9lcyBpdCBjb21wYXJlIHRvIHdoYXQgeW91IGZvdW5kIGluIFF1ZXN0aW9uIDE/CmBgYHtyfQojVGhlIGRlbnNpdHkgb2YgYSBkaXJlY3RlZCBuZXR3b3JrIGlzIGhhbGYgdGhhdCBvZiBhbiB1bmRpcmVjdGVkIG9uZSBiZWNhdXNlIHRoZXJlIGFyZSB0d2ljZSBhcyBtYW55IHBvc3NpYmxlIGVkZ2VzLiBJbiB0aGUgZGlyZWN0ZWQgbmV0d29yayB0aGUgZGVuc2l0eSBpcyAwLjA0MDcwNjYxLCB3aGljaCBpcyBsb3dlciB0aGFuIHRoZSBkZW5zaXR5IGluIHRoZSB1bmRpcmVjdGVkIG5ldHdvcmsgKDAuMDgxNDEzMjEpLiBUaGUgZGlyZWN0ZWQgbmV0d29yayBkZW5zaXR5IGlzIGxvd2VyLCBiZWNhdXNlIHdoZW4gd2Ugd2VudCBmcm9tIGFuIHVuZGlyZWN0ZWQgdG8gYSBkaXJlY3RlZCBuZXR3b3JrLCB3ZSBhcmUgYWRkaW5nIGVkZ2VzIGV2ZW4gdGhvdWdoIHdlIGRpZCBub3QgYWRkIG5vZGVzLgpgYGAKCkthcmF0ZSBjbHViIGRlbnNpdHksIGF1dG9tYXRpY2FsbHkgYW5kIG1hbnVhbGx5OgpgYGB7cn0KZ3JhcGguZGVuc2l0eShnKQoKZWNvdW50KGcpLygodmNvdW50KGcpKih2Y291bnQoZyktMSkpLzIpCmBgYAoKTGV0J3MgbWFrZSB0aGlzIGNsZWFyaWVyIHRvIHVuZGVyc3RhbmQgd2hhdCdzIGhhcHBlbmluZzoKYGBge3J9CnRvdGFsZWRnZXMgPC0gZWNvdW50KGcpCnBvc3NpYmxlZWRnZXMgPC0gKHZjb3VudChnKSoodmNvdW50KGcpLTEpKS8yCmVjb3VudChnKS9wb3NzaWJsZWVkZ2VzCgp0b3RhbGVkZ2VzL3Bvc3NpYmxlZWRnZXMKYGBgCgpTYW1lLCBjb3VudGluZyBkaXJlY3RseSBmcm9tIHRoZSBhZGphY2VuY3kgbWF0cml4OgpgYGB7cn0Kc3VtKGNvbFN1bXMoZCkpLyhucm93KGQpKm5jb2woZCkpCgplZGdlcyA8LSBzdW0oY29sU3VtcyhkKSkgICMgbm8gLWRpYWcoZCkgYmVjYXVzZSB0aGUgZGlhZ25vYWwgaXMgYWxyZWFkeSAwCnBvc3NpYmxlZWRnZXMyIDwtIG5yb3coZCkqbmNvbChkKSAKZWRnZXMvcG9zc2libGVlZGdlczIKYGBgCgpPdXIgZGlyZWN0ZWQgZ3JhcGg6CmBgYHtyfQpncmFwaC5kZW5zaXR5KGdfdGVtcCkKCmVkZ2VzIDwtIHN1bSh0ZW1wKQpwb3NzaWJsZWVkZ2VzIDwtIG5yb3codGVtcCkqbmNvbCh0ZW1wKS1ucm93KHRlbXApCmVkZ2VzL3Bvc3NpYmxlZWRnZXMgIApgYGAKCk91ciB1bmRpcmVjdGVkIGdyYXBoOgpgYGB7cn0KIyMgTmVlZCBhIG5ldyBncmFwaCBiZWNhdXNlIHVwZGF0ZWQgdGVtcDIgYWJvdmUgdG8gYmUgdW5kaXJlY3RlZApnX3RlbXAyYiA8LSBncmFwaF9mcm9tX2FkamFjZW5jeV9tYXRyaXgodGVtcDIsIG1vZGU9J3VuZGlyZWN0ZWQnKQoKZ3JhcGguZGVuc2l0eShnX3RlbXAyYikKCmVkZ2VzIDwtIHN1bSh0ZW1wMikvMgpwb3NzaWJsZWVkZ2VzIDwtIChucm93KHRlbXAyKSpucm93KHRlbXAyKS1ucm93KHRlbXAyKSkvMgplZGdlcy9wb3NzaWJsZWVkZ2VzCmBgYAoKRG8geW91IG5vdGljZSBhbnl0aGluZyBhYm91dCB0aGUgZGVuc2l0eSBvZiB0aGUgZGlyZWN0ZWQgZ3JhcGg/IEl0IGlzIGxvd2VyLiAqKldoeSBpcyB0aGUgZGVuc2l0eSBvZiB0aGUgdW5kaXJlY3RlZCBncmFwaCBoaWdoZXIgdGhhbiB0aGF0IG9mIHRoZSBkaXJlY3RlZCBvbmUgZXZlbiB0aG91Z2ggdGhleSB1c2UgdGhlIHNhbWUgZGF0YT8qKiBCZWNhdXNlIHdoZW4gd2Ugd2VudCBmcm9tIHVuZGlyZWN0ZWQgdG8gZGlyZWN0ZWQgYWRqYWNlbmN5IG1hdHJpY2VzLCB3ZSBhZGRlZCBhIDEgdG8gdGhlIGJvdHRvbSBsZWZ0IG9yIHRvcCByaWdodCB0cmlhbmdsZSBpZiBpdCBkaWQgbm90IGV4aXN0IGJlZm9yZS4gIFNvLCB3ZSBhcmUgYWRkaW5nIGVkZ2VzIGV2ZW4gdGhvdWdoIHdlIGFyZSBub3QgYWRkaW5nIG5vZGVzLgoKR2VuZXJhdGUgYSAxMCB4IDEwIG1hdHJpeC4KYGBge3J9Cm51bWJlciA8LSAxMAp0aGlzbWF0cml4IDwtIG1hdHJpeChyYmlub20obnVtYmVyXjIsIDEsIC41KSwgbnJvdz1udW1iZXIpCmBgYAoKQ2FsY3VsYXRlIGl0cyBkaXJlY3RlZCBkZW5zaXR5LgpgYGB7cn0KdG1fZCA8LSBncmFwaF9mcm9tX2FkamFjZW5jeV9tYXRyaXgodGhpc21hdHJpeCwgbW9kZT0nZGlyZWN0ZWQnKQpncmFwaC5kZW5zaXR5KHRtX2QpCgplZGdlcyA8LSBzdW0odGhpc21hdHJpeCkKcG9zc2libGVlZGdlcyA8LSBucm93KHRoaXNtYXRyaXgpKm5jb2wodGhpc21hdHJpeCktbnJvdyh0aGlzbWF0cml4KQplZGdlcy9wb3NzaWJsZWVkZ2VzIApgYGAKCkNhbGN1bGF0ZSBpdHMgdW5kaXJlY3RlZCBkZW5zaXR5LiAgUmVtZW1iZXIsIHRoZSBjYWxjdWxhdGlvbiBpcyB0aGUgc2FtZSBvbmNlIHlvdSBkZWZpbmUgdGhlIGVkZ2VzIGNvcnJlY3RseS4gRmlyc3QsIG1ha2Ugc3ltbWV0cmljIGJlZm9yZSBwYXNzaW5nIHRvIGlncmFwaCBhbmQgcmVtb3ZlIHNlbGYgbG9vcHMuCmBgYHtyfQp0aGlzbWF0cml4MiA8LSB0aGlzbWF0cml4CnRoaXNtYXRyaXgyW2xvd2VyLnRyaSh0aGlzbWF0cml4MildIDwtIHQodGhpc21hdHJpeDIpW2xvd2VyLnRyaSh0aGlzbWF0cml4MildICAKZGlhZyh0aGlzbWF0cml4MikgPC0gMAogIAp0bV91IDwtIGdyYXBoX2Zyb21fYWRqYWNlbmN5X21hdHJpeCh0aGlzbWF0cml4MiwgbW9kZT0ndW5kaXJlY3RlZCcpCmdyYXBoLmRlbnNpdHkodG1fdSwgbG9vcHM9RkFMU0UpCgplZGdlcyA8LSBzdW0odGhpc21hdHJpeDIpLzIKcG9zc2libGVlZGdlcyA8LSAobnJvdyh0aGlzbWF0cml4MikqbnJvdyh0aGlzbWF0cml4MiktbnJvdyh0aGlzbWF0cml4MikpLzIKZWRnZXMvcG9zc2libGVlZGdlcwpgYGAKCgoKCgoKCgoKCgoKCgojIyMjIENMVVNURVJJTkcKCgpXaGF0IGlzIHRoZSBnbG9iYWwgY2x1c3RlcmluZyBvZiB0aGUgdW5kaXJlY3RlZCBkb2xwaGluIGRhdGEgKHR3byBvcHRpb25zKT8KYGBge3J9CnRyYW5zaXRpdml0eShkYXRhcHMyLCB0eXBlPSJnbG9iYWx1bmRpcmVjdGVkIikKdHJhbnNpdGl2aXR5KGRhdGFwczIsIHR5cGUgPSAiZ2xvYmFsIikKYGBgCgpXaGF0IGlzIHRoZSBub2RlIHdpdGggdGhlIGhpZ2hlc3QgbG9jYWwgY2x1c3RlcmluZyB2YWx1ZSAodHdvIG9wdGlvbnMpPwpgYGB7cn0KVCA8LSB0cmFuc2l0aXZpdHkoZGF0YXBzMiwgdHlwZT0nbG9jYWwnKQpUMSA8LSByZXBsYWNlKFQsIGlzLm5hKFQpLCBGQUxTRSkKVihkYXRhcHMyKVtUMSA9PSBtYXgoVDEpXQoKCmNsdXN0ZXJpbmcgPC0gdHJhbnNpdGl2aXR5KGRhdGFwczIsIHR5cGUgPSAibG9jYWwiKSAKY2x1c3RlcmluZ1tpcy5uYShjbHVzdGVyaW5nKV0gPC0gMApkYXRhcHMyIDwtIHNldF92ZXJ0ZXhfYXR0cihkYXRhcHMyLCAiY2x1c3RlcmluZyIsIHZhbHVlID0gY2x1c3RlcmluZykgCmNsdXN0ZXJpbmdNYXggPC0gbWF4KGNsdXN0ZXJpbmcsIG5hLnJtID0gVFJVRSkKdG9rZWVwIDwtIFYoZGF0YXBzMikkY2x1c3RlcmluZyA9PSBjbHVzdGVyaW5nTWF4IAp0b2tlZXBbaXMubmEodG9rZWVwKV0gPC0gRkFMU0UgClYoZGF0YXBzMilbdG9rZWVwXQoKIyB+QmVsb3cgZmFpbHMgYmVjYXVzZSBvZiBOQSBpbiB0aGUgQm9vbGVhbiBzZXF1ZW5jZX4KVihkYXRhKVtWKGRhdGEpJGNsdXN0ZXJpbmcgPT0gY2x1c3RlcmluZ01heF0KYGBgCgpOb3RlIHRoYXQgb25lIGNhbiBzcGVjaWZpY3kgdW5kaXJlY3RlZCBidXQgaXQgZG9lcyBub3QgY2hhbmdlIHRoZSBjYWxjdWxhdGlvbjsgYGlncmFwaGAgZG9lcyBub3QgYXBwZWFyIHRvIGNhbGN1bGF0ZSBkaXJlY3RlZCBjbHVzdGVyaW5nLCBnbG9iYWxseSBvciBsb2NhbGx5OgpgYGB7cn0KdHJhbnNpdGl2aXR5KGcsIHR5cGU9Imdsb2JhbCIpCnRyYW5zaXRpdml0eShnLCB0eXBlPSJsb2NhbCIpCgp0cmFuc2l0aXZpdHkoZywgdHlwZT0iZ2xvYmFsdW5kaXJlY3RlZCIpCnRyYW5zaXRpdml0eShnLCB0eXBlPSJsb2NhbHVuZGlyZWN0ZWQiKQoKcGxvdChkZWdyZWUoZyksIHRyYW5zaXRpdml0eShnLCB0eXBlPSdsb2NhbCcpLCB4bGFiPSdEZWdyZWUgb2YgTm9kZScsIHlsYWI9J0xvY2FsIENsdXN0ZXJpbmcnKQoKdGhpcyA8LSB0cmFuc2l0aXZpdHkoZywgdHlwZT0nbG9jYWwnKQpwbG90KGRlZ3JlZShnKSwgdGhpcywgeGxhYj0nRGVncmVlIG9mIE5vZGUnLCB5bGFiPSdMb2NhbCBDbHVzdGVyaW5nJykKYGBgCgpDYWxjdWxhdGUgdGhlIGdsb2JhbCB0cmFuc2l0aXZpdHkgZm9yIG91ciBkaXJlY3RlZCBncmFwaC4KdHJhbnNpdGl2aXR5KHRtX2QsIHR5cGU9J2dsb2JhbCcpCgpDYWxjdWxhdGUgdGhlIGxvY2FsIHRyYW5zaXRpdml0eSBmb3Igb3VyIGRpcmVjdGVkIGdyYXBoLgp0cmFuc2l0aXZpdHkodG1fZCwgdHlwZT0nbG9jYWwnKQoKQ2FsY3VsYXRlIHRoZSBnbG9iYWwgdHJhbnNpdGl2aXR5IGZvciBvdXIgdW5kaXJlY3RlZCBncmFwaC4KdHJhbnNpdGl2aXR5KHRtX3UsIHR5cGU9J2dsb2JhbCcpCnRyYW5zaXRpdml0eSh0bV91LCB0eXBlPSdnbG9iYWx1bmRpcmVjdGVkJykKCkNhbGN1bGF0ZSB0aGUgbG9jYWwgdHJhbnNpdGl2aXR5IGZvciBvdXIgdW5kaXJlY3RlZCBncmFwaC4KdHJhbnNpdGl2aXR5KHRtX3UsIHR5cGU9J2xvY2FsJykKdHJhbnNpdGl2aXR5KHRtX3UsIHR5cGU9J2xvY2FsdW5kaXJlY3RlZCcpCgpGaW5kIHRoZSBub2RlIHdpdGggdGhlIGhpZ2hlc3QgY2x1c3RlcmluZyBjb2VmZmljaWVudCBmb3IgdGhlIGRpcmVjdGVkIG5ldHdvcmsuClYodG1fZClbdHJhbnNpdGl2aXR5KHRtX2QsIHR5cGU9J2xvY2FsJykgPT0gbWF4KHRyYW5zaXRpdml0eSh0bV9kLCB0eXBlPSdsb2NhbCcpKV0KCm10IDwtIG1heCh0cmFuc2l0aXZpdHkodG1fZCwgdHlwZT0nbG9jYWwnKSkKVih0bV9kKVt0cmFuc2l0aXZpdHkodG1fZCwgdHlwZT0nbG9jYWwnKSA9PSBtdF0KCkZpbmQgdGhlIG5vZGUgd2l0aCB0aGUgbG93ZXN0IGNsdXN0ZXJpbmcgY29lZmZpY2llbnQgdW5kaXJlY3RlZCBuZXR3b3JrLgpWKHRtX3UpW3RyYW5zaXRpdml0eSh0bV91LCB0eXBlPSdsb2NhbCcpID09IG1pbih0cmFuc2l0aXZpdHkodG1fdSwgdHlwZT0nbG9jYWwnKSldCgptaW50IDwtIG1pbih0cmFuc2l0aXZpdHkodG1fdSwgdHlwZT0nbG9jYWwnKSkKVih0bV91KVt0cmFuc2l0aXZpdHkodG1fdSwgdHlwZT0nbG9jYWwnKSA9PSBtaW50XQoKUGxvdCB0aGUgcmVsYXRpb25zaGlwIGJldHdlZW4gZGVncmVlIGFuZCBjbHVzdGVyaW5nIGZvciB0aGUgMTB4MTAgdW5kaXJlY3RlZCBuZXR3b3JrLgpwbG90KGRlZ3JlZSh0bV91KSwgdHJhbnNpdGl2aXR5KHRtX3UsIHR5cGU9J2xvY2FsJyksIHhsYWI9J05vZGUgRGVncmVlJywgeWxhYj0nTG9jYWwgQ2x1c3RlcmluZycsIHBjaD0yMCkKCgoKCgoKCgoKCgoKCgoKIyMjIyBSQU5ET00gTkVUV09SSwoKCkdlbmVyYXRlIGEgcmFuZG9tIG5ldHdvcmsgY29udGFpbmluZyAyMDAgbm9kZXMgd2l0aCBhIHByb2JhYmlsaXR5IG9mIC4yIHRoYXQgYW55IHR3byBub2RlcyBhcmUgY29ubmVjdGVkLiAgVXNlIGBpZ3JhcGhgIHRvIGNvbnZlcnQgaXQgdG8gYW4gdW5kaXJlY3RlZCBtYXRyaXguIE1ha2UgYSBoaXN0b2dyYW0gb2YgdGhlIGRlZ3JlZSBkaXN0cmlidXRpb24sIGxhYmVsIHRoZSBheGVzLCBnaXZlIGl0IGEgdGl0bGUsIGFuZCBzYXZlIGl0IHRvIGZpbGUgKHR3byBvcHRpb25zKS4KYGBge3J9Cm5vZGVzIDwtIDIwMApyYW5kbmV0IDwtIG1hdHJpeChyYmlub20obm9kZXNeMiwgMSwgLjIpLCBucm93PW5vZGVzKQpyYW5kbmV0W2xvd2VyLnRyaShyYW5kbmV0KV0gPC0gdChyYW5kbmV0KVtsb3dlci50cmkocmFuZG5ldCldCgpyYW5kbmV0X3UgPC0gZ3JhcGhfZnJvbV9hZGphY2VuY3lfbWF0cml4KHJhbmRuZXQsIG1vZGU9J3VwcGVyJykgCgoKbm9kZXMgPC0gMjAwCnByb2IgPC0gMC4yCnRoZW5ldHdvcmsgPC0gbWF0cml4KHJiaW5vbShub2Rlc14yLCAxLCBwID0gcHJvYiksIG5yb3cgPSBub2RlcykKZGlhZyh0aGVuZXR3b3JrKSA8LSAwCgp0aGVuZXR3b3JrIDwtIGdyYXBoX2Zyb21fYWRqYWNlbmN5X21hdHJpeCh0aGVuZXR3b3JrLCBtb2RlID0gInVwcGVyIikKCgpwZGYoJ34vRGVza3RvcC9VQ0xBL0NvdXJzZXMvNF9GYWxsIDIwMTkvUFAgMjkwL0Fzc2lnbm1lbnRzL2Fzc24zcGxvdDEucGRmJykKaGlzdChkZWdyZWUodGhlbmV0d29yayksIHhsYWIgPSAnRGVncmVlJywgeWxhYiA9ICdDb3VudCcsIGJyZWFrcyA9IDUwLCBtYWluID0gJ0Rpc3RyaWJ1dGlvbiBvZiBEZWdyZWUnKSAKZGV2Lm9mZigpCmBgYAoKUmVwZWF0IFByb2JsZW0gMSwgZXhjZXB0IHZhcnkgdGhlIHByb2JhYmlsaXR5IG9mIHRpZXMgZnJvbSAuMSB0byAxIGluIGluY3JlbWVudHMgb2YgLjEuICBTYXZlIGVhY2ggaGlzdG9ncmFtIGluIGEgZmlsZS4gIFdoYXQgZG8geW91IG5vdGljZSBhcyBwIGNoYW5nZXM/CmBgYHtyfQpub2RlcyA8LSAyMDAKcHJvYiA8LSBzZXEoLjEsMSxieT0uMSkKCmZvcihpIGluIDE6bGVuZ3RoKHByb2IpKXsKICB0aGVuZXR3b3JrIDwtIG1hdHJpeChyYmlub20obm9kZXNeMiwgMSwgcD1wcm9iW2ldKSwgbnJvdz1ub2RlcykKICBkaWFnKHRoZW5ldHdvcmspIDwtIDAKICB0aGVuZXR3b3JrIDwtIGdyYXBoX2Zyb21fYWRqYWNlbmN5X21hdHJpeCh0aGVuZXR3b3JrLCBtb2RlPSd1cHBlcicpCiAgCiAgcGRmKHBhc3RlMCgnfi9EZXNrdG9wL1VDTEEnLCBwcm9iW2ldLCAnLnBkZicpKQogIGhpc3QoZGVncmVlKHRoZW5ldHdvcmspLCB4bGFiPSdEZWdyZWUnLCB5bGFiPSdDb3VudCcsIG1haW49cGFzdGUwKCdEaXN0cmlidXRpb24gb2YgRGVncmVlLCBwPScsIHByb2JbaV0pLCBicmVha3M9NTApCiAgZGV2Lm9mZigpCn0KCmBgYAoKI01ha2luZyBTeW1tZXRyaWMgTmV0d29ya3MgRnJvbSBhbiBBZGphY2VuY3kgTWF0cml4CgpDb252ZXJ0IGFuIHVuYmFsYW5lZCBhZGphY2VuY3kgbWF0cml4IHRvIGFuIHVuZGlyZWN0ZWQgYWRqYWNlbmN5IG1hdHJpeC4gU3RhcnQgYnkgbWFraW5nIHRoZSBtYXRyaXg6CmBgYHtyfQpwcm9iYWJpbGl0eSA8LSAuMgp0aGlzbnVtYmVyIDwtIDEyCnRoaXNtYXRyaXggPC0gbWF0cml4KHJiaW5vbSh0aGlzbnVtYmVyXjIsIDEsIHByb2JhYmlsaXR5KSwgbnJvdz10aGlzbnVtYmVyKQpkaWFnKHRoaXNtYXRyaXgpIDwtIDAKYGBgCgpDb252ZXJ0IGl0IHRvIGFuIHVuZGlyZWN0ZWQgbmV0d29yay4KYGBge3J9CnQodGhpc21hdHJpeCkgCiMgdHJhbnNwb3NlcyB0aGlzIG1hdHJpeCwgaS5lLiBjZWxsMiwxIC0+IGNlbGwxLDI7IGNlbGwzLDEtPmNlbGwxLDMsIGFuZCBzbyBvbi4gIAoKIyBJIGRlY2lkZSB0byBtYWtlIHRoZSBsb3dlciB0cmlhbmdsZSBlcXVhbCB0aGUgdXBwZXIgdHJpYW5nbGUuICBTbyB0aGUgdHJhbnNwb3NlIGRvZXMgdGhhdDoKdGhpc21hdHJpeFtsb3dlci50cmkodGhpc21hdHJpeCldIDwtIHQodGhpc21hdHJpeClbbG93ZXIudHJpKHRoaXNtYXRyaXgpXQpgYGAKCgoKCgoKCgoKCgoKCgoKCiMjIyMgUkFORE9NIEdSQVBICgoKQnVpbGQgcmFuZG9tIGdyYXBocyBmcm9tIG1hdHJpY2VzLiBUaGVuLCB3ZSB1c2UgaWdyYXBoIHRvIGJ1aWxkIGEgcmFuZG9tIGdyYXBoLiAgSW4gYm90aCBjYXNlcywgd2Ugd2lsbCBsZWFybiBhYm91dCB0aGUgbmV0d29yayB2aWEgc29tZSBzdGF0aXN0aWNzLiAgQXQgdGhlIGVuZCBvZiB0aGlzIHNlY3Rpb24sIHdlIHdpbGwgbGVhcm4gaG93IHRvIGF1dG9tYXRpY2FsbHkgdmFyeSBwYXJhbWV0ZXJzIGFuZCB2aWV3IHRoZSBzdGF0aXN0aWNzIG9mIHRoZSByZXN1bHRpbmcgbmV0d29ya3MuIENyZWF0ZSB1c2luZyBtYXRyaXguCmBgYHtyfQpob3dtYW55cm93cyA8LSAxMDAKdGVtcCA8LSBtYXRyaXgocmJpbm9tKGhvd21hbnlyb3dzXjIsIDEsIC41KSwgbnJvdz1ob3dtYW55cm93cykKZGlhZyh0ZW1wKSA8LSAwICAjIE1ha2Ugc3VyZSB0aGVyZSBhcmUgbm8gc2VsZi1sb29wcy4KCiMgRG8gbm90IGZvcmdldCB0byBtYWtlIHN5bW1ldHJpYwp0ZW1wW2xvd2VyLnRyaSh0ZW1wKV0gPC0gdCh0ZW1wKVtsb3dlci50cmkodGVtcCldCgpidXM3MjAgPC0gZ3JhcGhfZnJvbV9hZGphY2VuY3lfbWF0cml4KHRlbXAsIG1vZGU9J3VuZGlyZWN0ZWQnKSAgIyBJIGhhdmUgY2hvc2VuIGEgd2VpcmQgb2JqZWN0IG5hbWUgdG8gZW1waGFzaXplIHRoYXQgdGhleSBhcmUgYXJiaXRyYXJ5LiAgTW9zdCBkb2N1bWVudGF0aW9uIGZvciBpZ3JhcGggY2FsbHMgZ3JhcGggb2JqZWN0cyBnLCBidXQgeW91IGRvIG5vdCBoYXZlIHRvLgoKYGBgCgpFeHBsb3JlIGRlZ3JlZToKYGBge3J9CmFkal9kZWdyZWVzIDwtIGNvbFN1bXModGVtcCkKYWRqX2RlZ3JlZXNbMToxMF0KZGVncmVlKGJ1czcyMClbMToxMF0KCnN1bW1hcnkoYWRqX2RlZ3JlZXMpCnN1bW1hcnkoZGVncmVlKGJ1czcyMCkpCgpgYGAKCkV4cGxvcmUgY2x1c3RlcmluZzoKYGBge3J9CnRyYW5zaXRpdml0eShidXM3MjAsIHR5cGU9Imdsb2JhbCIpCnRyYW5zaXRpdml0eShidXM3MjAsIHR5cGU9ImxvY2FsIilbMToxMF0KYGBgCgpFeHBsb3JlIGRlbnNpdHk6CmBgYHtyfQpncmFwaC5kZW5zaXR5KGJ1czcyMCkKYGBgCgoKTm93LCBsZXQgdXMgbGVhcm4gaG93IHRvIGhhdmUgYGlncmFwaGAgbWFrZSBhIHJhbmRvbSBncmFwaCBmb3IgdXMuICBJIHdpbGwgdGhlbiBzaG93IGhvdyB5b3UgY2FuIHZhcnkgdGhlIGRlbnNpdHkgb2YgdGhlIGdyYXBoIHVzaW5nIGEgbG9vcDsgeW91IGNvdWxkIGRvIHRoZSBzYW1lIGZvciBub2RlcywgYnV0IEkgbGVhdmUgdGhhdCBleGVyY2lzZSBmb3IgdGhlIHByb2JsZW0gc2V0LiAgSSB3aWxsIHRoZW4gc2hvdyBob3cgdG8gcHV0IHRoZSBvdXRwdXQgZnJvbSBlYWNoIHZhcmlhdGlvbiBpbnRvIGEgZGF0YWZyYW1lOyBvdGhlcndpc2UsIHlvdSBsb3NlIHRoZSByZXN1bHRzIGFzIGVhY2ggbmV3IGxvb3AgcnVucy4gQ3JlYXRlIHVzaW5nIGlncmFwaCBwYWNrYWdlLCBleHBsb3JlIGRlZ3JlZSwgY2x1c3RlcmluZywgZGVuc2l0eS4KYGBge3J9CmlidXM3MjAgPC0gZXJkb3MucmVueWkuZ2FtZShob3dtYW55cm93cyxwPS41KQoKc3VtbWFyeShkZWdyZWUoaWJ1czcyMCkpCnRyYW5zaXRpdml0eShpYnVzNzIwLCB0eXBlPSdnbG9iYWwnKQp0cmFuc2l0aXZpdHkoaWJ1czcyMCwgdHlwZT0nbG9jYWwnKVsxOjEwXQpncmFwaC5kZW5zaXR5KGlidXM3MjApCmBgYAoKCgoKCgoKCgoKCgoKCgoKIyMjIyBTTUFMTC1XT1JMRCBORVRXT1JLUwoKClJlY3JlYXRlIHRoZSBzbWFsbC13b3JsZCBjaGFydCBJIHNob3dlZCBpbiBjbGFzcywgdGhlIG9uZSB0aGF0IHNob3dzIGhvdyBhdmVyYWdlIHBhdGggbGVuZ3RoIGFuZCBjbHVzdGVyaW5nIGNoYW5nZSBhcyB0aGUgcHJvYmFiaWxpdHkgb2YgcmV3aXJpbmcgZWRnZXMgaW5jcmVhc2VzLiBNYWtlIHRoZSBuZXR3b3JrIGEgcmluZyB3aXRoIDEsMDAwIG5vZGVzIHdoZXJlIGVhY2ggbm9kZSBpcyBjb25uZWN0ZWQgdG8gdGVuIG5laWdoYm9ycy4gQXMgeW91IHdvcmsgb24gdGhpcyBwcm9ibGVtLCBrZWVwIGluIG1pbmQgc2l4IHRoaW5nczogCiAgICBhLiBZb3Ugd2lsbCBuZWVkIGEgYmFzZWxpbmUgbW9kZWwgYnkgd2hpY2ggdG8gbm9ybWFsaXplIChkaXZpZGUpIHRoZQogICAgdmFsdWVzIG9mIGVhY2ggc2ltdWxhdGlvbi4gIFRoYXQgaXMsIHlvdSB3aWxsIG5lZWQgYSBtb2RlbCB0aGF0IGRvZXMKICAgIG5vdCBjaGFuZ2UgZm9yIGVhY2ggaXRlcmF0aW9uIG9mIHRoZSBsb29wLgogICAgYi4gTWFrZSBzdXJlIHlvdSBoYXZlIHZlcnkgc21hbGwgdmFsdWVzIG9mIHAsIHNtYWxsZXIgdGhhbiB0aGUgY2xhc3MKICAgIGV4YW1wbGUuCiAgICBjLiBNYWtlIHN1cmUgdGhlIHgtYXhpcyBpcyB0aGUgbG9nYXJpdGhtIG9mIHAuCiAgICAgIDEuIElmIG9uZSBvZiB5b3VyIHZhbHVlcyBvZiBwIGlzIDAsIHlvdSBzaG91bGQgYWRkIGEgc21hbGwgYW1vdW50LAogICAgICBzdWNoIGFzIC4wMSBvciAuMSwgdG8gZWFjaCB2YWx1ZSBvZiB0aGUgcmV3aXJpbmcgcHJvYmFiaWxpdHkgd2hlbgogICAgICB5b3UgcGxvdC4gIGBsb2coMClgIGlzIHVuZGVmaW5lZCwgYnV0IHlvdSBuZWVkIHRvIHNlZSB0aGUgcmVzdWx0CiAgICAgIGZvciB0aGF0IHZhbHVlLgogICAgICAyLiBXaGVuIHlvdSBkbyB0aGlzLCB5b3UgbWF5IG5vdGljZSB0aGUgeC1heGlzIHZhbHVlcyBiZWNvbWUKICAgICAgbmVnYXRpdmUuICBJdCBpcyBhY2NlcHRhYmxlIGlmIHlvdSBsZWF2ZSB0aGUgdmFsdWVzIGxpa2UgdGhhdCwgYnV0CiAgICAgIEkgZW5jb3VyYWdlIHlvdSB0byBleHBsb3JlIGhvdyB0byBmaXggdGhlbS4gIFN1YmhpbnQ6IGxvb2sgYXQKICAgICAgYHhheHRgIGluIGBwbG90KClgIGFuZCBgYXhpcygpYCBhZnRlciBgcGxvdCgpYC4gIFN1YmhpbnQyOiBZb3UKICAgICAgY291bGQgYWxzbyBsb29rIGF0IHRoZSBgbG9nYCBhcmd1bWVudCBpbiBgcGxvdCgpYC4KICAgIGQuIFRvIGFkZCBhIHNlY29uZCBsaW5lIHRvIGEgZ3JhcGgsIHVzZSBgbGluZXMoKWAgYWZ0ZXIgYHBsb3QoKWAuCiAgICBlLiBTYXZlIHRoZSBjaGFydCB0byB5b3VyIGNvbXB1dGVyLgogICAgZi4gSG93IHdpbGwgeW91IGRpc3Rpbmd1aXNoIHdoaWNoIGxpbmUgY29ycmVzcG9uZHMgdG8gd2hpY2gKICAgIHN0YXRpc3RpYz8gIFVzZSB0aGUgYGx0eWAgYXJndW1lbnQgdG8gbGluZXMoKSBhbmQgcGxvdCgpLiAgSWYgeW91CiAgICBhcmUgYWR2ZW50dXJvdXMsIGFkZCBhIGxlZ2VuZCB0byB0aGUgcGxvdCBhcyB3ZWxsLiAKYGBge3J9CnByb2JzIDwtIHNlcSgwLCAxLCBieT0uMDAxKQpub2RlcyA8LSAxMDAwCm5laWdoYm9ycyA8LSAxMAoKYmFzZWxpbmUgPC0gc2FtcGxlX3NtYWxsd29ybGQoZGltPTEsIHNpemU9bm9kZXMsIG5laT1uZWlnaGJvcnMsIHA9MCkKYl9nQyA8LSBtZWFuKHRyYW5zaXRpdml0eShiYXNlbGluZSwgdHlwZT0nbG9jYWwnKSkKZ19hUEwgPC0gYXZlcmFnZS5wYXRoLmxlbmd0aChiYXNlbGluZSkKICAKcmVtZW1iZXIgPC0gZGF0YS5mcmFtZShpdGVyYXRpb249TlVMTCwgcmV3aXJlUHJvYmFiaWxpdHk9TlVMTCwgZ2xvYmFsQ2x1c3RlcmluZz1OVUxMLCBhdmdQYXRoTGVuZ3RoPU5VTEwpCgpmb3IoaSBpbiAxOmxlbmd0aChwcm9icykpewogIHByaW50KHBhc3RlKCdPbiBpdGVyYXRpb24nLCBpKSkKICAKICB0ZW1wIDwtIHNhbXBsZV9zbWFsbHdvcmxkKGRpbT0xLCBzaXplPW5vZGVzLCBuZWk9bmVpZ2hib3JzLCBwPXByb2JzW2ldKQoKICBnbG9iYWxDbHVzdGVyaW5nIDwtIG1lYW4odHJhbnNpdGl2aXR5KHRlbXAsIHR5cGU9J2xvY2FsJykpL2JfZ0MgIAogIGF2Z1BhdGhMZW5ndGggPC0gYXZlcmFnZS5wYXRoLmxlbmd0aCh0ZW1wKS9nX2FQTAogIAogIGQgPC0gZGF0YS5mcmFtZShpdGVyYXRpb249aSwgcmV3aXJlUHJvYmFiaWxpdHk9cHJvYnNbaV0sIGdsb2JhbENsdXN0ZXJpbmc9Z2xvYmFsQ2x1c3RlcmluZywgYXZnUGF0aExlbmd0aD1hdmdQYXRoTGVuZ3RoKQogIAogIHJlbWVtYmVyIDwtIHJiaW5kKHJlbWVtYmVyLCBkKQp9CgojIFRoZSBiZWxvdyBsaW5lIGFkZHMgYSBsaXR0bGUgYml0IHRvIGVhY2ggdmFsdWUgYmVjYXVzZSB0aGVyZSBpcyBhIDAgdGhhdCBJIHdhbnQgdG8ga2VlcCwgYnV0IGxvZygwKSBpcyB1bmRlZmluZWQuCgpyZW1lbWJlciRyZXdpcmVQcm9iYWJpbGl0eSA8LSByZW1lbWJlciRyZXdpcmVQcm9iYWJpbGl0eSsuMDAwMDEKCnBkZignfi9EZXNrdG9wL1VDTEEucGRmJykKcGxvdCh4PXJlbWVtYmVyJHJld2lyZVByb2JhYmlsaXR5LCB5PXJlbWVtYmVyJGF2Z1BhdGhMZW5ndGgsIHhsYWI9J1Jld2lyZSBQcm9iYWJpbGl0eScsIHlsYWI9J0NoYW5nZSBpbiBQYXRoIExlbmd0aCwgQ2x1c3RlcmluZycsIG1haW49J1NtYWxsLXdvcmxkIE5ldHdvcmtzJywgdHlwZT0nbCcsIGxvZz0neCcpCmxpbmVzKHg9cmVtZW1iZXIkcmV3aXJlUHJvYmFiaWxpdHksIHk9cmVtZW1iZXIkZ2xvYmFsQ2x1c3RlcmluZywgbHR5PTMpCmxlZ2VuZCgndG9wcmlnaHQnLCBsZWdlbmQ9YygnQXZlcmFnZSBQYXRoIExlbmd0aCcsICdDbHVzdGVyaW5nJyksIGx0eT1jKDEsMykpCmRldi5vZmYoKQpgYGAKCgpVc2UgaWdyYXBoOiBGcm9tIGhlcmUgb24gb3V0LCB3ZSB3aWxsIHJlbHkgb24gYGlncmFwaGAncyBmdW5jdGlvbnMgdG8gYnVpbGQgbmV0d29ya3MuICBXaGlsZSB3ZSBjb3VsZCBidWlsZCB0aGVtIG1hbnVhbGx5IGZyb20gYWRqYWNlbmN5IG1hdHJpY2VzLCBjcmVhdGluZyB0aGUgcHJvcGVyIGFkamFjZW5jeSBtYXRyaXggb3IgZWRnZWxpc3QgaXMgYSBiaXQgbW9yZSBjb21wbGljYXRlZC4gIEl0IGlzIGRvYWJsZSBidXQgbm90IG5lY2Vzc2FyeSBmb3IgdGhlIGFpbXMgb2YgdGhpcyBjbGFzcy4gVGhlIGBpZ3JhcGhgIGZ1bmN0aW9uIGBzYW1wbGVfc21hbGx3b3JsZGAgY2FuIGJlIGNvbmZ1c2luZy4gIFlvdSBkZWZpbmUgdGhlIG51bWJlciBvZiBkaW1lbnNpb25zIG9mIHRoZSBsYXR0aWNlOyAxIG1lYW5zIGEgbGluZSwgMiBtZWFucyBhIG1hdHJpeCwgYW5kIHNvIG9uLiAgVGhlIGRpbWVuc2lvbiB3aWxsIHJhaXNlIHRoZSBudW1iZXIgb2Ygbm9kZXMgeW91IHdhbnQgdG8gdGhlIHBvd2VyIG9mIHRoZSBkaW1lbnNpb24uICBJdCBpcyB0aGVyZWZvcmUgZWFzaWVzdCB0byBqdXN0IGRlZmluZSB0aGUgbnVtYmVyIG9mIG5vZGVzIHlvdSB3YW50IGFuZCBhc3N1bWUgb25lIGRpbWVuc2lvbi4gIGBuZWlgIGlzIGhvdyBmYXIgKG1hbnkgc3RlcHMpIGluIGVhY2ggZGlyZWN0aW9uIHRvIG1ha2UgY29ubmVjdGlvbnMgYmV0d2VlbiBub2RlczsgdGhlIGhpZ2hlciBpdCBpcywgdGhlIGdyZWF0ZXIgdGhlIGF2ZXJhZ2UgZGVncmVlLgoKCkNyZWF0ZSBhIHNtYWxsLXdvcmwgbmV0d29yayB1c2luZyBpZ3JhcGggcGFja2FnZSwgZXhwbG9yZSBkZWdyZWUsIGNsdXN0ZXJpbmcsIGRlbnNpdHk6CmBgYHtyfQpub2RlcyA8LSAxMDAKCmJ1czIwIDwtIHNhbXBsZV9zbWFsbHdvcmxkKGRpbT0xLCBzaXplPW5vZGVzLCBuZWk9MywgcD0uMikKc3VtbWFyeShkZWdyZWUoYnVzMjApKQp0cmFuc2l0aXZpdHkoYnVzMjAsIHR5cGU9J2dsb2JhbCcpCm1lYW4odHJhbnNpdGl2aXR5KGJ1czIwLCB0eXBlPSdsb2NhbCcpKQpncmFwaC5kZW5zaXR5KGJ1czIwKQoKYGBgCgpMZXQncyBtYWtlIHRoZSBwcmludGVkIG91dHB1dCBhIGxpdHRsZSBlYXNpZXIgdG8gcmVhZC4KYGBge3J9CnN1bW1hcnkoZGVncmVlKGJ1czIwKSkKcGFzdGUoJ1RoZSBncmFwaFwncyB0cmFuc2l0aXZpdHkgaXMnLCByb3VuZCh0cmFuc2l0aXZpdHkoYnVzMjAsIHR5cGU9J2dsb2JhbCcpLDIpKQpwYXN0ZSgnVGhlIG90aGVyIHdheSB0byBjYWxjdWxhdGUgdHJhbnNpdGl2aXR5IHN1Z2dlc3RzIHRoZSB2YWx1ZSBpcycsIG1lYW4odHJhbnNpdGl2aXR5KGJ1czIwLCB0eXBlPSdsb2NhbCcpKSkKcGFzdGUoJ1RoZSBkZW5zaXR5IG9mIHRoZSBncmFwaCBpcycsIGdyYXBoLmRlbnNpdHkoYnVzMjApKQpwYXN0ZSgnVGhlcmUgYXJlJywgZWNvdW50KGJ1czIwKSwgJ2VkZ2VzIGluIHRoZSBncmFwaCcpCgpgYGAKCkxldCdzIGRvIHRoYXQgYWdhaW4sIGNoYW5naW5nIHRoZSBzaXplIG9mIHRoZSBuZXR3b3JrIGZybyAxMDAgdG8gMSwwMDAuCmBgYHtyfQpub2RlcyA8LSAxMDAwCgpidXMyMCA8LSBzYW1wbGVfc21hbGx3b3JsZChkaW09MSwgc2l6ZT1ub2RlcywgbmVpPTMsIHA9LjIpCmBgYAoKTGV0J3MgbWFrZSB0aGUgcHJpbnRlZCBvdXRwdXQgYSBsaXR0bGUgZWFzaWVyIHRvIHJlYWQuCmBgYHtyfQpzdW1tYXJ5KGRlZ3JlZShidXMyMCkpCnBhc3RlKCdUaGUgZ3JhcGhcJ3MgdHJhbnNpdGl2aXR5IGlzJywgcm91bmQodHJhbnNpdGl2aXR5KGJ1czIwLCB0eXBlPSdnbG9iYWwnKSwgNSkpCnBhc3RlKCdUaGUgb3RoZXIgd2F5IHRvIGNhbGN1bGF0ZSB0cmFuc2l0aXZpdHkgc3VnZ2VzdHMgdGhlIHZhbHVlIGlzJywgbWVhbih0cmFuc2l0aXZpdHkoYnVzMjAsIHR5cGU9J2xvY2FsJykpKQpwYXN0ZSgnVGhlIG90aGVyIHdheSB0byBjYWxjdWxhdGUgdHJhbnNpdGl2aXR5IHN1Z2dlc3RzIHRoZSB2YWx1ZSBpcycsIG1lYW4odHJhbnNpdGl2aXR5KGJ1czIwLCB0eXBlPSdsb2NhbCcpLCBuYS5ybT1UUlVFKSkKcGFzdGUoJ1RoZSBkZW5zaXR5IG9mIHRoZSBncmFwaCBpcycsIGdyYXBoLmRlbnNpdHkoYnVzMjApKQpwYXN0ZSgnVGhlcmUgYXJlJywgZWNvdW50KGJ1czIwKSwgJ2VkZ2VzIGluIHRoZSBncmFwaCcpCmBgYAoKTm93LCB3ZSBjYW4gdmFyeSB0aGUgcGFyYW1ldGVycyBsaWtlIHdlIGRpZCBmb3IgcmFuZG9tIGdyYXBocy4gIFNpbmNlIHdlIGFyZSBmYW1pbGlhciB3aXRoIHRoaXMgcHJvY2VzcywgSSB3aWxsICoqdmFyeSBgcGAgYW5kIHNhdmUgdGhlIHJlc3VsdHMgdG8gYSBkYXRhZnJhbWUqKiBpbW1lZGlhdGVseS4gIE5vdGljZSB0aGF0IEkgZGVmaW5lIGhvdyBtYW55IGl0ZXJhdGlvbnMgb2YgdGhlIGxvb3AgZGlmZmVyZW50bHkgdGhhbiBmb3IgcmFuZG9tIGdyYXBoczsgdGhleSBib3RoIHdvcmsgdGhlIHNhbWUuICBJIGFtIGdvaW5nIHRvIGludHJvZHVjZSBhIG5ldyB3YXkgdG8gZ2VuZXJhdGUgc2VxdWVuY2VzIG9mIG51bWJlcnMgYXMgd2VsbC4KClZhcnkgcCB1c2luZyBzZXF1ZW5jZS4gIFRoZXJlIGFyZSB0d28gd2F5cyB0byBkbyB0aGlzLgpgYGB7cn0KcCA8LSBzZXEoZnJvbT0wLCB0bz0xLCBieT0uMSkKcDIgPC0gc2VxKGZyb209MCwgdG89MSwgbGVuZ3RoLm91dD0xMSkKcCA9PSBwMiAgIyBHcmVhdAoKbm9kZXMgPC0gMjUwCgpyZW1lbWJlciA8LSBkYXRhLmZyYW1lKGl0ZXJhdGlvbj1OVUxMLCByZXdpcmVQcm9iYWJpbGl0eT1OVUxMLCBhdmdEZWdyZWU9TlVMTCwgZ2xvYmFsQ2x1c3RlcmluZz1OVUxMLCBnbGJhbENsdXN0ZXJpbmcyPU5VTEwsIGRlbnNpdHk9TlVMTCwgZGlhbWV0ZXI9TlVMTCwgYXZnUGF0aExlbmd0aD1OVUxMKQoKZm9yKGkgaW4gMTpsZW5ndGgocCkpewogIHRlbXAgPC0gc2FtcGxlX3NtYWxsd29ybGQoZGltPTEsIHNpemU9bm9kZXMsIG5laT0zLCBwPXBbaV0pCiAgCiAgcHJpbnQocGFzdGUoJ1dlIGFyZSBvbiBsb29wJywgaSkpCiAgaXRlcmF0aW9uIDwtIGkKICByZXdpcmVQcm9iYWJpbGl0eSA8LSBwW2ldCiAgYXZnRGVncmVlIDwtIG1lYW4oZGVncmVlKHRlbXApKQogIGdsb2JhbENsdXN0ZXJpbmcgPC0gdHJhbnNpdGl2aXR5KHRlbXAsIHR5cGU9J2dsb2JhbCcpCiAgZ2xvYmFsQ2x1c3RlcmluZzIgPC0gbWVhbih0cmFuc2l0aXZpdHkodGVtcCwgdHlwZT0nbG9jYWwnKSwgbmEucm09VFJVRSkKICBkZW5zaXR5IDwtIGdyYXBoLmRlbnNpdHkodGVtcCkKICBkaWFtZXRlciA8LSBkaWFtZXRlcih0ZW1wKQogIGF2Z1BhdGhMZW5ndGggPC0gYXZlcmFnZS5wYXRoLmxlbmd0aCh0ZW1wKQoKICBwcmludCgnSnVzdCBmaW5pc2hlZCBjYWxjdWxhdGlvbnMuICBNYWtpbmcgZGF0YWZyYW1lLicpCiAgCiAgZCA8LSBkYXRhLmZyYW1lKGl0ZXJhdGlvbj1pdGVyYXRpb24sIHJld2lyZVByb2JhYmlsaXR5PXJld2lyZVByb2JhYmlsaXR5LCBhdmdEZWdyZWU9YXZnRGVncmVlLCBnbG9iYWxDbHVzdGVyaW5nPWdsb2JhbENsdXN0ZXJpbmcsIGdsb2JhbENsdXN0ZXJpbmcyPWdsb2JhbENsdXN0ZXJpbmcyLCBkZW5zaXR5PWRlbnNpdHksIGRpYW1ldGVyPWRpYW1ldGVyLCBhdmdQYXRoTGVuZ3RoPWF2Z1BhdGhMZW5ndGgpCiAgCiAgcmVtZW1iZXIgPC0gcmJpbmQocmVtZW1iZXIsIGQpICAjICJyb3diaW5kIiwgYWRkcyBhIG5ldyByb3csIGQsIHRvIHdoYXRldmVyIGV4aXN0cyBpbiByZW1lbWJlci4KfQoKcmVtZW1iZXIgICMgV29vIQpgYGAKCioqV2h5IGRvZXMgdGhlIGRlbnNpdHkgbmV2ZXIgY2hhbmdlPyoqIEJlY2F1c2UgaXQgaXMgdGhlIHBlcmNlbnRhZ2Ugb2YgcG9zc2libGUgZWRnZXMgdGhhdCBhY3R1YWxseSBleGlzdDsgZGVuc2l0eSB0ZWxscyB5b3UgYWJvdXQgdGhlIG5ldHdvcmsgb3ZlcmFsbCAod2hpbGUgZGVncmVlIHRlbGxzIHlvdSBzb21ldGhpbmcgYWJvdXQgaW5kaXZpZHVhbCBub2RlcykKCkNyZWF0ZSBhIHNtYWxsLXdvcmxkIHJpbmcgbmV0d29yayB3aXRoIDEwMCBub2RlcyB0aGF0IGVhY2ggc3RhcnQgd2l0aCBjb25uZWN0aW9ucyB0byAxMCBuZWlnaGJvcnMuICBSYW5kb21seSByZXdpcmUgd2l0aCBwcm9iYWJpbGl0eSBvZiAuMDMuCmBgYHtyfQpzdyA8LSBzYW1wbGVfc21hbGx3b3JsZChkaW09MSwgc2l6ZT0xMDAsIG5laT01LCBwPS4wMykKc3cKYGBgCgpXcml0ZSBhIGxvb3AgdGhhdCBjcmVhdGVzIGEgbmV3IHNtYWxsLXdvcmxkIG5ldHdvcmsgZWFjaCBpdGVyYXRpb24uICBWYXJ5IHRoZSByZXdpcmluZyBwcm9iYWJpbGl0eSBmcm9tIDAgdG8gLjIgaW4gaW5jcmVtZW50cyBvZiAuMDEuICBTYXZlIHRoZSByZXN1bHRzIGluIGEgZGF0YWZyYW1lLCB0cmFja2luZyBhdCBsZWFzdCB0aGUgb3ZlcmFsbCBjbHVzdGVyaW5nLCBkaWFtZXRlciwgYW5kIGF2ZXJhZ2UgcGF0aCBsZW5ndGguCmBgYHtyfQp0aGVwcyA8LSBzZXEoMCwgLjIsIGJ5PS4wMSkKCnJlbWVtYmVyMyA8LSBkYXRhLmZyYW1lKGl0ZXJhdGlvbj1OVUxMLCByZXdpcmVQcm9iYWJpbGl0eT1OVUxMLCBhdmdEZWdyZWU9TlVMTCwgZ2xvYmFsQ2x1c3RlcmluZz1OVUxMLCBnbGJhbENsdXN0ZXJpbmcyPU5VTEwsIGRlbnNpdHk9TlVMTCwgZGlhbWV0ZXI9TlVMTCwgYXZnUGF0aExlbmd0aD1OVUxMKQoKbm9kZXMgPC0gMTAwCmZvcihpIGluIDE6bGVuZ3RoKHRoZXBzKSl7CiAgdGVtcCA8LSBzYW1wbGVfc21hbGx3b3JsZChkaW09MSwgc2l6ZT1ub2RlcywgbmVpPTMsIHA9dGhlcHNbaV0pCiAgCiAgcHJpbnQocGFzdGUoJ1dlIGFyZSBvbiBsb29wJywgaSkpCiAgaXRlcmF0aW9uIDwtIGkKICByZXdpcmVQcm9iYWJpbGl0eSA8LSB0aGVwc1tpXQogIGF2Z0RlZ3JlZSA8LSBtZWFuKGRlZ3JlZSh0ZW1wKSkKICBnbG9iYWxDbHVzdGVyaW5nIDwtIHRyYW5zaXRpdml0eSh0ZW1wLCB0eXBlPSdnbG9iYWwnKQogIGdsb2JhbENsdXN0ZXJpbmcyIDwtIG1lYW4odHJhbnNpdGl2aXR5KHRlbXAsIHR5cGU9J2xvY2FsJyksIG5hLnJtPVRSVUUpCiAgZGVuc2l0eSA8LSBncmFwaC5kZW5zaXR5KHRlbXApCiAgZGlhbWV0ZXIgPC0gZGlhbWV0ZXIodGVtcCkKICBhdmdQYXRoTGVuZ3RoIDwtIGF2ZXJhZ2UucGF0aC5sZW5ndGgodGVtcCkKCiAgI3ByaW50KCdKdXN0IGZpbmlzaGVkIGNhbGN1bGF0aW9ucy4gIE1ha2luZyBkYXRhZnJhbWUuJykKICAKICBkIDwtIGRhdGEuZnJhbWUoaXRlcmF0aW9uPWl0ZXJhdGlvbiwgcmV3aXJlUHJvYmFiaWxpdHk9cmV3aXJlUHJvYmFiaWxpdHksIGF2Z0RlZ3JlZT1hdmdEZWdyZWUsIGdsb2JhbENsdXN0ZXJpbmc9Z2xvYmFsQ2x1c3RlcmluZywgZ2xvYmFsQ2x1c3RlcmluZzI9Z2xvYmFsQ2x1c3RlcmluZzIsIGRlbnNpdHk9ZGVuc2l0eSwgZGlhbWV0ZXI9ZGlhbWV0ZXIsIGF2Z1BhdGhMZW5ndGg9YXZnUGF0aExlbmd0aCkKICAKICByZW1lbWJlcjMgPC0gcmJpbmQocmVtZW1iZXIzLCBkKSAgIyAicm93YmluZCIsIGFkZHMgYSBuZXcgcm93LCBkLCB0byB3aGF0ZXZlciBleGlzdHMgaW4gcmVtZW1iZXIuCn0KCnJlbWVtYmVyMwpgYGAKCgoKCgoKCgoKCgoKCgojIyMjIFNDQUxFIEZSRUUgTkVUV09SS1MKCgpDcmVhdGUgYSBzY2FsZSBmcmVlIG5ldHdvcmsgd2l0aCAxMiwwMDAgbm9kZXMgd2hlcmUgZWFjaCBub2RlIGFkZHMgMjAgZWRnZXMuICAKICAgIGEuIE1ha2UgYSBoaXN0b2dyYW0gb2YgdGhlIGRlZ3JlZSBkaXN0cmlidXRpb24uCiAgICBiLiBXaGF0IGlzIHRoZSByYXRpbyBvZiB0aGUgbWVhbiB0byBtZWRpYW4gZGVncmVlPwogICAgYy4gSXMgdGhlIGRlbnNpdHkgbGVzcyB0aGFuIGEgc21hbGwtd29ybGQgbmV0d29yaz8gIFdoeT8KYGBge3J9Cm5vZGVzIDwtIDEyMDAwCm5ld2VkZ2VzIDwtIDIwCnNmIDwtIHNhbXBsZV9wYShuPTEyMDAwLCBwb3dlcj0xLCBtPW5ld2VkZ2VzLCBkaXJlY3RlZD1GQUxTRSkKCnBkZignRmlndXJlcy9QUzNfc2NhbGVmcmVlLnBkZicpCmhpc3QoZGVncmVlKHNmKSwgYnJlYWtzPTEwMCwgeGxhYj0nRGVncmVlJywgeWxhYj0nQ291bnQnLCBtYWluPSdTY2FsZS1mcmVlIE5ldHdvcmsnKQpkZXYub2ZmKCkKCm1lYW4oZGVncmVlKHNmKSkvbWVkaWFuKGRlZ3JlZShzZikpCgpncmFwaC5kZW5zaXR5KHNmKQoKIyBEZW5zaXR5IGlzIGxlc3MgaW4gYSBzY2FsZS1mcmVlIG5ldHdvcmsgYmVjYXVzZSBlZGdlcyBhcmUgYWRkZWQgcmFuZG9tbHksIHdoZXJlYXMgc21hbGwtd29ybGQgbmV0d29ya3Mgc3RhcnQgZnJvbSBuZXR3b3JrcyB0aGF0IGhhdmUgY2x1c3RlcmluZyB2YWx1ZXMgb2YgMS4gVGhlIGRlbnNpdHkgaXMgbGVzcyBpbiBhIHNjYWxlLWZyZWUgbmV0d29yayB0aGFuIGluIGEgc21hbGwtd29ybGQgbmV0d29yaywgYmVjYXVzZSBlZGdlcyBhcmUgYWRkZWQgYXQgcmFuZG9tLCBpLmUuLCBzbWFsbC13b3JsZCBuZXR3b3JrcyBzdGFydCBmcm9tIG5ldHdvcmtzIHRoYXQgaGF2ZSBhIGNsdXN0ZXJpbmcgdmFsdWUgb2YgMSBhbmQgd2lsbCB0aGVyZWZvcmUgYWx3YXlzIGhhdmUgYSBncmVhdGVyIGRlbnNpdHkuCgojVGhlIHJ1bGUgYWJvdmUgYXBwbGllcyB0byBnbG9iYWwgY2x1c3RlcmluZyBhcyB3ZWxsLCBpLmUuLCBnbG9iYWwgY2x1c3RlcmluZyBpcyBsZXNzIGluIGEgc2NhbGUtZnJlZSBuZXR3b3JrIHRoYW4gaW4gYSBzbWFsbC13b3JsZCBuZXR3b3JrLgoKYGBgCgpOb3csIHdlIHdpbGwgZXhwbG9yZSBzY2FsZS1mcmVlIG5ldHdvcmtzLCB1c2luZyBCYXJhYmFzaSdzIG1vZGVsIG9mIHByZWZlcmVudGlhbCBhdHRhY2htZW50LiAgV2hpbGUgdGhlcmUgYXJlIG1hbnkgbWVjaGFuaXNtcyB0aGF0IGNhbiBnZW5lcmF0ZSBzY2FsZS1mcmVlIGRpc3RyaWJ1dGlvbnMsIGhpcyBpcyB0aGUgbW9zdCBjb21tb25seSB1c2VkIGFuZCB0aGVyZWZvcmUgdGhlIG9uZSBtb3N0IGNvbW1vbmx5IGluY2x1ZGVkIG91dCBvZiB0aGUgYm94IGluIHNvZnR3YXJlLiBXZSB3aWxsIGZvY3VzIG9uIHZhcnlpbmcgdGhlIG51bWJlciBvZiBlZGdlcyBhZGRlZCB0byBhIG5ldHdvcmsuICBBIGtleSBmZWF0dXJlIG9mIHNjYWxlLWZyZWUgbmV0d29ya3MgaXMgdGhhdCB0aGUgbWVhbiBkZWdyZWUgaXMgbXVjaCBoaWdoZXIgdGhhbiB0aGUgbWVkaWFuOyB3ZSB3aWxsIHRyYWNrIHRoYXQgcmF0aW8uCmBgYHtyfQpOb2RlcyA8LSAxMDAwCnNmIDwtIHNhbXBsZV9wYShuPW5vZGVzLCBwb3dlcj0xLCBtPTEwLCBkaXJlY3RlZD1GQUxTRSkKCmRlZ3JlZV9kaXN0cmlidXRpb24oc2YpICAjIEFzIGxvbmcgYXMgbWF4KGRlZ3JlZShzZikpLiAgR2l2ZXMgcHJvYmFiaWxpdHkgb2Ygbm9kZSBoYXZpbmcgdGhhdCBkZWdyZWUuCmBgYAoKTGV0J3MgbWFrZSB0aGUgcHJpbnRlZCBvdXRwdXQgYSBsaXR0bGUgZWFzaWVyIHRvIHJlYWQuCmBgYHtyfQpzdW1tYXJ5KGRlZ3JlZShzZikpCnBhc3RlKCdUaGUgZ3JhcGhcJ3MgdHJhbnNpdGl2aXR5IGlzJywgdHJhbnNpdGl2aXR5KHNmLCB0eXBlPSdnbG9iYWwnKSkKcGFzdGUoJ1RoZSBvdGhlciB3YXkgdG8gY2FsY3VsYXRlIHRyYW5zaXRpdml0eSBzdWdnZXN0cyB0aGUgdmFsdWUgaXMnLCBtZWFuKHRyYW5zaXRpdml0eShzZiwgdHlwZT0nbG9jYWwnKSkpCnBhc3RlKCdUaGUgZGVuc2l0eSBvZiB0aGUgZ3JhcGggaXMnLCBncmFwaC5kZW5zaXR5KHNmKSkKcGFzdGUoJ1RoZXJlIGFyZScsIGVjb3VudChzZiksICdlZGdlcyBpbiB0aGUgZ3JhcGgnKQpwYXN0ZSgnVGhlIHJhdGlvIG9mIG1lYW4gdG8gbWVkaWFuIGRlZ3JlZSBpcycsIG1lYW4oZGVncmVlKHNmKSkvbWVkaWFuKGRlZ3JlZShzZikpKQoKIyBMZXQncyBjaGFuZ2UgbQpzZiA8LSBzYW1wbGVfcGEobj1ub2RlcywgcG93ZXI9MSwgbT0xLCBkaXJlY3RlZD1GQUxTRSkKCmRlZ3JlZV9kaXN0cmlidXRpb24oc2YpICAjIEFzIGxvbmcgYXMgbWF4KGRlZ3JlZShzZikpLiAgR2l2ZXMgcHJvYmFiaWxpdHkgb2Ygbm9kZSBoYXZpbmcgdGhhdCBkZWdyZWUuCgojIExldCdzIG1ha2UgdGhlIHByaW50ZWQgb3V0cHV0IGEgbGl0dGxlIGVhc2llciB0byByZWFkLgpzdW1tYXJ5KGRlZ3JlZShzZikpCnBhc3RlKCdUaGUgZ3JhcGhcJ3MgdHJhbnNpdGl2aXR5IGlzJywgdHJhbnNpdGl2aXR5KHNmLCB0eXBlPSdnbG9iYWwnKSkKcGFzdGUoJ1RoZSBvdGhlciB3YXkgdG8gY2FsY3VsYXRlIHRyYW5zaXRpdml0eSBzdWdnZXN0cyB0aGUgdmFsdWUgaXMnLCBtZWFuKHRyYW5zaXRpdml0eShzZiwgdHlwZT0nbG9jYWwnKSkpCnBhc3RlKCdUaGUgZGVuc2l0eSBvZiB0aGUgZ3JhcGggaXMnLCBncmFwaC5kZW5zaXR5KHNmKSkKcGFzdGUoJ1RoZXJlIGFyZScsIGVjb3VudChzZiksICdlZGdlcyBpbiB0aGUgZ3JhcGgnKQpwYXN0ZSgnVGhlIHJhdGlvIG9mIG1lYW4gdG8gbWVkaWFuIGRlZ3JlZSBpcycsIG1lYW4oZGVncmVlKHNmKSkvbWVkaWFuKGRlZ3JlZShzZikpKQoKIyBMZXQncyBjaGFuZ2UgbSBBR0FJTgpzZiA8LSBzYW1wbGVfcGEobj1ub2RlcywgcG93ZXI9MSwgbT0yMCwgZGlyZWN0ZWQ9RkFMU0UpCgpkZWdyZWVfZGlzdHJpYnV0aW9uKHNmKSAgIyBBcyBsb25nIGFzIG1heChkZWdyZWUoc2YpKS4gIEdpdmVzIHByb2JhYmlsaXR5IG9mIG5vZGUgaGF2aW5nIHRoYXQgZGVncmVlLgoKIyBMZXQncyBtYWtlIHRoZSBwcmludGVkIG91dHB1dCBhIGxpdHRsZSBlYXNpZXIgdG8gcmVhZC4Kc3VtbWFyeShkZWdyZWUoc2YpKQpwYXN0ZSgnVGhlIGdyYXBoXCdzIHRyYW5zaXRpdml0eSBpcycsIHRyYW5zaXRpdml0eShzZiwgdHlwZT0nZ2xvYmFsJykpCnBhc3RlKCdUaGUgb3RoZXIgd2F5IHRvIGNhbGN1bGF0ZSB0cmFuc2l0aXZpdHkgc3VnZ2VzdHMgdGhlIHZhbHVlIGlzJywgbWVhbih0cmFuc2l0aXZpdHkoc2YsIHR5cGU9J2xvY2FsJykpKQpwYXN0ZSgnVGhlIGRlbnNpdHkgb2YgdGhlIGdyYXBoIGlzJywgZ3JhcGguZGVuc2l0eShzZikpCnBhc3RlKCdUaGVyZSBhcmUnLCBlY291bnQoc2YpLCAnZWRnZXMgaW4gdGhlIGdyYXBoJykKcGFzdGUoJ1RoZSByYXRpbyBvZiBtZWFuIHRvIG1lZGlhbiBkZWdyZWUgaXMnLCBtZWFuKGRlZ3JlZShzZikpL21lZGlhbihkZWdyZWUoc2YpKSkKCiMgTGV0J3MgbWFrZSBhIGhpc3RvZ3JhbSBvZiB0aGUgZGVncmVlIGRpc3RyaWJ1dGlvbgojcGRmKC9jaGFuZ2UvdGhpcy9maWxlbmFtZS90by9zYXZlL291dC5wZGYpCmhpc3QoZGVncmVlKHNmKSwgeGxhYj0nTm9kZSBEZWdyZWUnLCB5bGFiPSdDb3VudCcsIGJyZWFrcz01MCwgbWFpbj0nRGlzdHJpYnV0aW9uIG9mIERlZ3JlZSBpbiBTY2FsZS1GcmVlIEdyYXBoJykKI2Rldi5vZmYoKQpgYGAKCk5vdywgbGV0J3MgdXNlIGEgbG9vcCB0byBzZWUgaG93IHRoZXNlIG1lYXN1cmVzIGNoYW5nZSBhcyB3ZSBpbmNyZWFzZSB0aGUgbnVtYmVyIG9mIG5vZGVzIGluIHRoZSBuZXR3b3JrLiAgTGV0IHVzIGluY3JlYXNlIGl0IGluIHVuaXRzIG9mIDEsMDAwLiBWYXJ5IG5ldHdvcmsgc2l6ZSB1c2luZyBhIGxvb3A6CmBgYHtyfQpub2RlcyA8LSBzZXEoZnJvbT0xMDAwLHRvPTEwMDAwLGJ5PTEwMDApCgpyZW1lbWJlciA8LSBkYXRhLmZyYW1lKGl0ZXJhdGlvbj1OVUxMLCBuZXR3b3JrU2l6ZT1OVUxMLCBhdmdEZWdyZWU9TlVMTCwgZ2xvYmFsQ2x1c3RlcmluZz1OVUxMLCBnbGJhbENsdXN0ZXJpbmcyPU5VTEwsIGRlbnNpdHk9TlVMTCwgcmF0aW89TlVMTCwgYXZnUGF0aExlbmd0aD1OVUxMKQoKZm9yKGkgaW4gMTpsZW5ndGgobm9kZXMpKXsKICB0ZW1wIDwtIHNhbXBsZV9wYShuPW5vZGVzW2ldLCBwb3dlcj0xLCBtPTEwLCBkaXJlY3RlZD1GQUxTRSkKICAKICBwcmludChwYXN0ZSgnVGhpcyBpcyBsb29wJywgaSwgJ29mJywgbGVuZ3RoKG5vZGVzKSkpCiAgCiAgZCA8LSBkYXRhLmZyYW1lKGl0ZXJhdGlvbj1pLCBzaXplPW5vZGVzW2ldLCBhdmdEZWdyZWU9bWVhbihkZWdyZWUodGVtcCkpLCBnbG9iYWxDbHVzdGVyaW5nPXRyYW5zaXRpdml0eSh0ZW1wLCB0eXBlPSdnbG9iYWwnKSwgZ2xvYmFsQ2x1c3RlcmluZzI9bWVhbih0cmFuc2l0aXZpdHkodGVtcCwgdHlwZT0nbG9jYWwnKSksIGRlbnNpdHk9Z3JhcGguZGVuc2l0eSh0ZW1wKSwgcmF0aW89bWVhbihkZWdyZWUodGVtcCkpL21lZGlhbihkZWdyZWUodGVtcCkpLCBhdmdQYXRoTGVuZ3RoPWF2ZXJhZ2UucGF0aC5sZW5ndGgodGVtcCkpCgogIHJlbWVtYmVyIDwtIHJiaW5kKHJlbWVtYmVyLCBkKSAgIyAicm93YmluZCIsIGFkZHMgYSBuZXcgcm93LCBkLCB0byB3aGF0ZXZlciBleGlzdHMgaW4gcmVtZW1iZXIuCn0KCnJlbWVtYmVyICMgTm90IGJhZC4KYGBgCgoKCgoKCgoKCgoKCiMjIyMgUkVBTCBORVRXT1JLUwoKSW4gdGhpcyBzZWN0aW9uLCB3ZSB3aWxsIGV4cGxvcmUgYWN0dWFsIG5ldHdvcmsgZGF0YS4gIFRoZSBmaXJzdCBkYXRhc2V0IGlzIGUtbWFpbHMgZnJvbSBFbnJvbiwgYW4gZW5lcmd5IGZpcm0gdGhhdCBpbXBsb2RlZCB3aGVuIGl0cyBhY2NvdW50aW5nIG1hbmlwdWxhdGlvbnMgd2VyZSBkaXNjb3ZlcmVkLiAgVGhlIHNlY29uZCBpcyBhIHNhbXBsZSBvZiBpbmRpdmlkdWFscyBmcm9tIEZhY2Vib29rLgoKTG9hZCBFbnJvbjoKYGBge3J9CmVucm9uIDwtIHJlYWRfZ3JhcGgoJ2h0dHBzOi8vcmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbS9aYWNoYXJ5U1QvRGF0YS9tYXN0ZXIvZW5yb25fZW1haWwnLCBmb3JtYXQ9YygnZWRnZWxpc3QnKSkKI0Fub3RoZXIgd2F5OiBsb2FkIEVucm9uIGRhdGEgdXNpbmcgcmVhZC50YWJsZSwgY29udmVydCB0byBpZ3JhcGggb2JqZWN0LgoKIyBFeHBsb3JlCmlzLmRpcmVjdGVkKGVucm9uKQplbnJvbiA8LSBhcy51bmRpcmVjdGVkKGVucm9uKSAjbWFrZSBpdCB1bmRpcmVjdGVkCmlzLmRpcmVjdGVkKGVucm9uKQoKbGVuZ3RoKFYoZW5yb24pKSAjbm9kZXMKdmNvdW50KGVucm9uKSAjc2FtZSBhcyBhYm92ZQoKZWNvdW50KGVucm9uKSAjZWRnZXMKYGBgCgpgYGB7cn0KIyBBcmUgdGhlcmUgYWN0dWFsbHkgbmVnYXRpdmUgdmFsdWVzPwpzdW1tYXJ5KGVucm9uKSAgIyBObyBuZWdhdGl2ZSB2YWx1ZXMuCgojIFNvLCBhZGQgb25lIHRvIGVhY2ggdmVydGV4IGlkLiAgVGhpcyBpcyBmaW5lIGJlY2F1c2UgdGhleSBhcmUganVzdCBJRCBudW1iZXJzLgojIE5COiBJIGNyZWF0ZSBhIG5ldyBvYmplY3QsIGVucm9uMiwgdG8gc2hvdyB5b3UgdGhlIGRpZmZlcmVuY2UuICBJZiBJIHdhcyBub3QgdGVhY2hpbmcsIEkgd291bGQganVzdCBoYXZlIHdyaXR0ZW4gZW5yb24gPC0gZW5yb24gKyAxLgplbnJvbjIgPC0gZW5yb24gKyAxCmhlYWQoZW5yb24yKQpoZWFkKGVucm9uKQoKZW5yb24yIDwtIGdyYXBoX2Zyb21fZWRnZWxpc3QoYXMubWF0cml4KGVucm9uMiksIGRpcmVjdGVkPUZBTFNFKQppcy5kaXJlY3RlZChlbnJvbjIpCnZjb3VudChlbnJvbjIpCmVjb3VudChlbnJvbjIpCgojIExldCdzIGxvb2sgYXQgc3VtbWFyeSBzdGF0aXNpY3MuCnN1bW1hcnkoZGVncmVlKGVucm9uMikpCnBhc3RlKCdUaGUgZ3JhcGhcJ3MgdHJhbnNpdGl2aXR5IGlzJywgdHJhbnNpdGl2aXR5KGVucm9uMiwgdHlwZT0nZ2xvYmFsJykpCnBhc3RlKCdUaGUgb3RoZXIgd2F5IHRvIGNhbGN1bGF0ZSB0cmFuc2l0aXZpdHkgc3VnZ2VzdHMgdGhlIHZhbHVlIGlzJywgbWVhbih0cmFuc2l0aXZpdHkoZW5yb24yLCB0eXBlPSdsb2NhbCcpKSkKcGFzdGUoJ1RoZSBkZW5zaXR5IG9mIHRoZSBncmFwaCBpcycsIGdyYXBoLmRlbnNpdHkoZW5yb24yKSkKcGFzdGUoJ1RoZXJlIGFyZScsIGVjb3VudChlbnJvbjIpLCAnZWRnZXMgaW4gdGhlIGdyYXBoJykKcGFzdGUoJ1RoZSBkaWFtZXRlciBpcycsIGRpYW1ldGVyKGVucm9uMikpICAjIFRha2VzIGF3aGlsZSB0byBydW4uCnBhc3RlKCdUaGUgYXZlcmFnZSBwYXRoIGxlbmd0aCBpcycsIGF2ZXJhZ2UucGF0aC5sZW5ndGgoZW5yb24yKSkgICMgVGFrZXMgYXdoaWxlIHRvIHJ1bi4KYGBgCgpXZSBjYW4gYWxzbyB0YWtlIHN1YnNldHMgb2YgYSBuZXR3b3JrIGJhc2VkIG9uIG5vZGUgZmVhdHVyZXMuICBCZWxvdywgSSB3aWxsIHNob3cgeW91IGhvdyB0byBrZWVwIG9ubHkgbm9kZXMgbWVldGluZyBkZWdyZWUgdGhyZXNob2xkcy4KYGBge3J9CiMgU2hvdyBob3cgdG8gc2VsZWN0IG9ubHkgaGlnaC1kZWdyZWUgbm9kZXMKZGVncmVlMTAgPC0gaW5kdWNlZF9zdWJncmFwaChlbnJvbjIsIGRlZ3JlZShlbnJvbjIpPj0xMCkKdmNvdW50KGRlZ3JlZTEwKQplY291bnQoZGVncmVlMTApCgp2Y291bnQoZGVncmVlMTApL3Zjb3VudChlbnJvbjIpICAjIDguOTQlIG9mIG5vZGVzCmVjb3VudChkZWdyZWUxMCkvZWNvdW50KGVucm9uMikgICMgNjIuNDUlIG9mIGVkZ2VzCgpzdW1tYXJ5KGRlZ3JlZShkZWdyZWUxMCkpCnBhc3RlKCdUaGUgZ3JhcGhcJ3MgdHJhbnNpdGl2aXR5IGlzJywgdHJhbnNpdGl2aXR5KGRlZ3JlZTEwLCB0eXBlPSdnbG9iYWwnKSkKcGFzdGUoJ1RoZSBvdGhlciB3YXkgdG8gY2FsY3VsYXRlIHRyYW5zaXRpdml0eSBzdWdnZXN0cyB0aGUgdmFsdWUgaXMnLCBtZWFuKHRyYW5zaXRpdml0eShkZWdyZWUxMCwgdHlwZT0nbG9jYWwnKSwgbmEucm09VFJVRSkpCnBhc3RlKCdUaGUgZGVuc2l0eSBvZiB0aGUgZ3JhcGggaXMnLCBncmFwaC5kZW5zaXR5KGRlZ3JlZTEwKSkKcGFzdGUoJ1RoZXJlIGFyZScsIGVjb3VudChkZWdyZWUxMCksICdlZGdlcyBpbiB0aGUgZ3JhcGgnKQpwYXN0ZSgnVGhlIGRpYW1ldGVyIGlzJywgZGlhbWV0ZXIoZGVncmVlMTApKSAgIyBUYWtlcyBhd2hpbGUgdG8gcnVuLgpwYXN0ZSgnVGhlIGF2ZXJhZ2UgcGF0aCBsZW5ndGggaXMnLCBhdmVyYWdlLnBhdGgubGVuZ3RoKGRlZ3JlZTEwKSkgICMgVGFrZXMgYXdoaWxlIHRvIHJ1bi4KYGBgCgpZb3UgY2FuIGtlZXAgbm9kZXMgdGhhdCBtYXRjaCBhbnkgc29ydCBvZiBmaWx0ZXIgeW91IHdhbnQuICBOZXh0LCBJIHNob3cgYmFzZWQgb24gY2x1c3RlcmluZy4gU2hvdyBob3cgdG8gc2VsZWN0IG5vZGVzIHdpdGggaGlnaCBjbHVzdGVyaW5nCmBgYHtyfQpoaWdoY2x1c3RlcmluZyA8LSBtYXgodHJhbnNpdGl2aXR5KGVucm9uMiwgdHlwZT0nbG9jYWwnKSkKaGlnaGNsdXN0ZXJpbmcgIyBIbT8gIAp0cmFuc2l0aXZpdHkoZW5yb24yLCB0eXBlPSJsb2NhbCIpWzE6MTAwXSAjT2ssIHRoZSBjb2RlIHdvcmtzLiAgU29tZXRpbWVzLCB0aGlzIG1lYW5zIHRoZXJlIGlzIGEgTkEgdmFsdWUsIGFuZCBtYW55IFIgZnVuY3Rpb25zIG5lZWQgaGVscCB3aXRoIHRoYXQuCgpoaWdoY2x1c3RlcmluZyA8LSBtYXgodHJhbnNpdGl2aXR5KGVucm9uMiwgdHlwZT0nbG9jYWwnKSwgbmEucm09VFJVRSkKCiMgVGFrZSBub2RlcyB3aXRoIGhhbGYgdGhlIG1heCBjbHVzdGVyaW5nIHZhbHVlIG9yIG1vcmUuCiMgSSBoYXZlIHRvIGFkZCB0aGUgZXh0cmEgc3RlcHMgYmVjYXVzZSBvZiB0aGUgTkEgdmFsdWVzIHRoZSBmaXJzdCBsaW5lIHJldHVybnMuCmtlZXAgPC0gdHJhbnNpdGl2aXR5KGVucm9uMiwgdHlwZT0nbG9jYWwnKSA+IGhpZ2hjbHVzdGVyaW5nKi41CmtlZXBbaXMubmEoa2VlcCldIDwtIEZBTFNFICAjIFIgd2lsbCBub3Qga25vdyB3aGF0IHRvIGRvIHdpdGggdGhlIE5BIHZhbHVlcy4Ka2VlcFsxOjMwMDBdICMgU28gd2Uga25vdyB3aGF0IGtlZXAgbG9va3MgbGlrZS4KaGlnaGNsdXN0ZXJpbmdfZyA8LSBpbmR1Y2VkX3N1YmdyYXBoKGVucm9uMiwgVihlbnJvbjIpW2tlZXBdKQoKdmNvdW50KGhpZ2hjbHVzdGVyaW5nX2cpCmVjb3VudChoaWdoY2x1c3RlcmluZ19nKSAgIyBWZXJ5IGxvdy4gIFdoeT8KCnRhYmxlKGRlZ3JlZShoaWdoY2x1c3RlcmluZ19nKSkgICMgVGhpcyBpcyB3aHkuCmBgYAoKCgoKCgoKCgoKCiMjIyMgQ0FMQ1VMQVRJT05TICh2ZWN0b3JzLCBvYmplY3RzLCBwaHJhc2VzKQoKCldoYXQgaXMgMzgxOSB0aW1lcyAxMDI/CmBgYHtyfQozODE5ICogMTAyCmBgYAoKV2hhdCBpcyAzODE5IG1pbnVzIDE4OTI/CmBgYHtyfQozODE5IC0gMTg5MgpgYGAKClJlcGVhdCB0aGUgYWJvdmUsIGJ1dCBzYXZlIGVhY2ggbnVtYmVyIGFzIGFuIG9iamVjdC4KYGBge3J9Cm9iajEgPC0gMzgxOSAKb2JqMiA8LSAxODkyIApvYmoxICogb2JqMgpvYmoxIC0gb2JqMgpgYGAKCk1ha2UgYSB2ZWN0b3Igb2YgdGhlIG51bWJlcnMgMTAsIDIwLCAzMCwgNDAsIGFuZCA1MC4gTXVsdGlwbHkgdGhhdCB2ZWN0b3IgIyBieSBhbm90aGVyIG9mIHRoZSBudW1iZXJzIDMsIDQsIDUsIDYsIGFuZCA3LgpgYGB7cn0KdjEgPC0gYygxMCwgMjAsIDMwLCA0MCwgNTApCnYxX2FsdCA8LSBzZXEoZnJvbSA9IDEwLCB0byA9IDUwLCBieSA9IDEwKQp2MiA8LSBjKDMsIDQsIDUsIDYsIDcpIHYyX2FsdCA8LSAzOjcKCnYxICogdjIKdjFfYWx0ICogdjJfYWx0CmBgYAoKUmVwZWF0IGFib3ZlLCBleGNlcHQgbWFrZSB0aGUgc2Vjb25kIHZlY3RvciB0aGUgbnVtYmVycyA1IGFuZCAxMC4gU2hvdyB0aGUgcmVzdWx0cyBhbmQgZXhwbGFpbiwgaW4gd29yZHMsIHdoYXQgUiBpcyBkb2luZyB3aXRoIHRoaXMgc2hvcnRlciB2ZWN0b3IuCmBgYHtyfQp2MiA8LSBjKDUsIDEwKQp2MSAqIHYyCgojKFdhcm5pbmcgaW4gdjEgKiB2MjogbG9uZ2VyIG9iamVjdCBsZW5ndGggaXMgbm90IGEgbXVsdGlwbGUgb2Ygc2hvcnRlciBvYmplY3QgbGVuZ3RoKTogUiByZXBlYXRzIHRoZSB2MiB2ZWN0b3IgYXMgbWFueSB0aW1lcyBhcyBpdCB0YWtlcyB0byBtYXRjaCB0aGUgbGVuZ3RoIG9mIHYxLiBTbywgaXQgY3JlYXRlcyBhIHZlY3RvciB0aGF0IGlzICg1LDEwLDUsMTAsNSkKYGBgCgpEb2VzIHRoZSBwaHJhc2UsICJJIGVuam95IGxlYXJuaW5nIHRvIHByb2dyYW0iIGNvbnRhaW4gdGhlIHdvcmQgImxlYXJuaW5nIj8gIFdoYXQgYWJvdXQgImxlYXJuIj8gIFdoYXQgYWJvdXQgInRvd2FyZHMiPyAKYGBge3J9Cj4gcGhyYXNlIDwtIGMoJ0kgZW5qb3kgbGVhcm5pbmcgdG8gcHJvZ3JhbScpCj4gZ3JlcGwoJ2xlYXJuaW5nJywgcGhyYXNlKQpbMV0gVFJVRSAKPiBncmVwbCgnbGVhcm4nLCBwaHJhc2UpClsxXSBUUlVFCj4gZ3JlcGwoJ3Rvd2FyZHMnLCBwaHJhc2UpClsxXSBGQUxTRQpgYGAKCldoeSBkb2VzIFIgcmV0dXJuIGBbMV1gPwpgYGB7cn0KI0JlY2F1c2UgaXQgaXMgdGVsbGluZyB0aGUgcmVzdWx0IGlzIGEgdmVjdG9yIG9mIGxlbmd0aCAxLiBWZWN0b3JzIGFuZCBjYWxjdWxhdGlvbiBiZWNvbWUgZXNwZWNpYWxseSB1c2VmdWwgd2hlbiB5b3VyIHZlY3RvciBoYXMgbGVuZ3RoIGdyZWF0ZXIgdGhhbiAxLCBzdWNoIGFzIHdoZW4gaXQgcmVwcmVzZW50cyBhIHZhcmlhYmxlIHdpdGggbXVsdGlwbGUgb2JzZXJ2YXRpb25zLiAgWW91IGNhbiB0aGVuIGVhc2lseSBtYW5pcHVsYXRlIGFsbCBlbnRyaWVzIGluIG9uZSB2ZWN0b3Igb3IgcGVyZm9ybSBvcGVyYXRpb25zIG9uIG11bHRpcGxlIHZlY3RvcnMuICBUbyBtYWtlIGEgdmVjdG9yIG9mIGxlbmd0aCBncmVhdGVyIHRoYW4gMSwgeW91IG5lZWQgYGMoKWAuICBUaGUgImMiIHN0YW5kcyBmb3IgImNvbmNhdGVuYXRlIi4KYGBgCgpQcmFjdGljZSB3aXRoIHZlY3RvcnMgb2YgbGVuZ3RoIGdyZWF0ZXIgdGhhbiAxOgpgYGB7cn0KZmlyc3QgPC0gYygxMDAsMTksMjAsODksNzYsMTAyLDc0LDgxLDY1KQpmaXJzdCAqIDIgIAojVGhpcyBtdWx0aXBsaWVzIGVhY2ggZW50cnkgYnkgMiAoZ3JlYXQgdG8gY29udmVydCBpbmNoZXMgdG8gY2VudGltZXRlcnMpCmZpcnN0IC0gMjAgIAojVGhpcyBzdWJ0cmFjdHMgZWFjaCBlbnRyeSBieSAyMAoKc2Vjb25kIDwtIGMoMSwyLDQsMTAsMTAsMSw0LDUsMTApIAojRG8gbm90IG5lZWQgdG8gaGF2ZSB0aGUgZXhhY3Qgc2FtZSBudW1iZXIgb2YgZW50cmllcyBhZ2FpbiwgYnV0IG1hZGUgaXQgbW9yZSBzaW1wbGUpCgpmaXJzdCAqIHNlY29uZCAgCiNNdWx0aXBsaWVzIGVhY2ggZW50cnkgaW4gZmlyc3QgYnkgc2Vjb25kIGluIG9yZGVyLCBpZSAxKjEwMDsgbXVsdGlwbHlpbmcgZWFjaCBlbnRyeSBpcyB3ZWlnaHRpbmcgZGF0YQpmaXJzdCAvIHNlY29uZApgYGAKCkV4YW1wbGUgdXNpbmcgdmVjdG9ycyB0byBjYWxjdWxhdGUgYSBtb3JlIGNvbXBsaWNhdGVkIG91dGNvbWU6CmBgYHtyfQpvYnNlcnZlZCA8LSBmaXJzdCAKIyBJIGhhdmUgY3JlYXRlZCBhIG5ldyBvYmplY3QgdGhhdCBoYXMgdGhlIHZhbHVlcyBvZiBmaXJzdCwgaS5lLiwgaXMgbWFwcGVkIHRvIGZpcnN0IGFib3ZlLgoKb2JzZXJ2ZWQgPT0gZmlyc3QgIAojIFJldHVybnMgVFJVRSBmb3IgYXMgbWFueSBlbnRyaWVzIGFzIHRoZXJlIGFyZSBpbiBlYWNoIHZlY3Rvci4gKE5lZWQgdHdvIGVxdWFsIG1hcmtzLCBvbmUgZXF1YWwgd2lsbCBqdXN0IG1ha2UgYSBjb3B5IGxpa2UgPC0sIHdoaWxlIHRoaXMgY29tcGFyZXMsIGdvb2Qgd2F5IHRvIGNoZWNrIGRhdGEgYWZ0ZXIgZG9pbmcgYSB0cmFuc2Zvcm1hdGlvbikKCm9ic2VydmVkICE9IGZpcnN0IAojKGJvb2xlaWFuIG9wZXJhdGlvbiBmb3IgZG9lcyBub3QgZXF1YWwpCmBgYAoKQ2hpLXNxdWFyZQpgYGB7cn0KZXhwZWN0ZWQgPC0gYyg5MCwxOCwzMCw3OSw4MCw4OCw4MCw4Myw3MCkgCm51bWVyYXRvciA8LSAob2JzZXJ2ZWQtZXhwZWN0ZWQpXjIKc3VtKG51bWVyYXRvci9leHBlY3RlZCkgIApgYGAKClZlY3RvcnMgZG8gbm90IGhhdmUgdG8gYmUgbnVtYmVycy4gRm9yIGV4YW1wbGUsIHlvdSBtYXkgaGF2ZSBhIHZhcmlhYmxlIHRoYXQgaXMgYSByZXNwb25kZW50J3MgZmF2b3JpdGUgc3BvcnRzIHRlYW0sIHNvIHRoZSBlbnRyaWVzIGFyZSBsaWtlbHkgdG8gYmUgd29yZHMuICBJZiB5b3UgaGF2ZSBhIGRhdGFmcmFtZSwgdGhlIGNvbHVtbiBuYW1lcyBhcmUgYSBzdHJpbmcgdmVjdG9yOgpgYGB7cn0KY2hhcl92ZWMgPC0gYygidGhlc2UiLCAiYXJlIiwgInNvbWUiLCAid29yZHMiKQpsZW5ndGgoY2hhcl92ZWMpCgond29yZHMnICVpbiUgY2hhcl92ZWMKJ3dvcmRzJyA9PSBjaGFyX3ZlYwojZm9yIGhvdyBvZnRlbgond29yZCcgJWluJSAgY2hhcl92ZWMKI15nb29kIGZvciBzZWFyY2hpbmcgbGFyZ2VyIHRleHRzCgpzYW1ldHlwZSA8LSBjKCd0aGlzJywgMjAsICdjb252ZXJ0cyB0bycsIDUsICdzdHJpbmdzJykKI15pZiBSIGlzIHVuc3VyZSBvZiBhIHRoaW5nIGl0IGNvbnZlcnRzIGl0IGludG8gYSBzdHJpbmcKYGBgCgpDcmVhdGUgYW4gb2JqZWN0IHRoYXQgY29udGFpbnMgZml2ZSBudW1iZXJzLiBNdWx0aXBseSB0aG9zZSBudW1iZXJzIGJ5IDIuICBBZGQgODIgdG8gdGhvc2UgbnVtYmVycy4KYGBge3J9CnRoaXMgPC0gYygxLDIsMyw0LDUpCnRoaXMqMgp0aGlzKzgyCmBgYAoKQ3JlYXRlIGEgc2Vjb25kIG9iamVjdCB0aGF0IGNvbnRhaW5zIGZpdmUgbnVtYmVycy4gTXVsdGlwbHkgdGhlIHR3byBvYmplY3RzIHRvZ2V0aGVyIGFuZCBzYXZlIHRoZSByZXN1bHQgYXMgYSBuZXcgb2JqZWN0LiBEaXZpZGUgdGhlIHR3byBvYmplY3RzLCBzYXZpbmcgdGhlIHJlc3VsdCBhcyBhIG5ldyBvYmplY3QuCmBgYHtyfQphcmJpdHJhcnkgPC0gYygxOCwxOCw5MywxMDMsIDQ1KQpuZXcgPC0gdGhpcyphcmJpdHJhcnkKbmV3IDwtIHRoaXMvYXJiaXRyYXJ5CmBgYAoKRG9lcyB0aGUgdmVjdG9yIGNvbnRhaW4gdGhlIHdvcmQgInRvbWF0byI/CmBgYHtyfQpjaGFyYWN0ZXJzIDwtIGMoJ3RoaXMnLCAndmVjdG9yJywgJ2NvbnRhaW5zJywgJ3dvcmRzJywgJ3RoYXQnLCAnaGF2ZScsICd3b3JkcycsICd3aXRoJywgJ2xldHRlcnMnLCAnYW5kJywgJ251bWJlcnMnLCAyOSwgJzMwJyApCgondG9tYXRvJyAlaW4lIGNoYXJhY3RlcnMKYGBgCgpIb3cgbWFueSB0aW1lcyBkb2VzIHRoZSB2ZWN0b3IgY29udGFpbiB0aGUgd29yZCAid29yZHMiPwpgYGB7cn0KZ3JlcCgnd29yZHMnLCBjaGFyYWN0ZXJzKQpsZW5ndGgoZ3JlcCgnd29yZHMnLCBjaGFyYWN0ZXJzKSkKYGBgCgpTZWFyY2ggdGhlIHZlY3RvciBmb3IgdGhlIG51bWJlciAyOS4gCmBgYHtyfQpncmVwKCcyOScsIGNoYXJhY3RlcnMpCmBgYAoKU2VhcmNoIHRoZSB2ZWN0b3IgZm9yIHRoZSBudW1iZXIgMTAwLgpgYGB7cn0KZ3JlcCgnMTAwJywgY2hhcmFjdGVycykKCiNpbnRlZ2VyKDApIC0tPiB3YXkgUiBzYXlzIG5vCmBgYAoKSXQgd29ya3MgZGlmZmVyZW50bHkgaWYgdGhlIHdvcmRzIGFyZSBub3Qgc2VwYXJhdGVkIGluIGEgdmVjdG9yOgpgYGB7cn0Kd29yZHMgPC0gYygndGhpcyB2ZWN0b3IgY29udGFpbnMgd29yZHMgdGhhdCBoYXZlIHdvcmRzIHdpdGggbGV0dGVycyBhbmQgbnVtYmVycyAyOSAzMCcpCid0aGlzJyAlaW4lIHdvcmRzCgojRkFMU0UgYmVjYXVzZSB0aGUgd29yZCB0aGlzIGRvZXMgbm90ID0gdGhlIGZpcnN0IHVuaXQgaW4gdmVjdG9yIHdvcmRzIAoKbGVuZ3RoKHdvcmRzKQoKZ3JlcCgndGhpcycsIHdvcmRzKQpncmVwbCgndGhpcycsIHdvcmRzKQoKIyBOb3RpY2UgdGhhdCBncmVwIHBhcnRpYWwgbWF0Y2hlcyBieSBkZWZhdWx0LiBUbyBnZXQgZXhhY3Qgd29yZCwgdXNlIHNwYWNlczsgZ3JlcCB3aWxsIHRlbGwgbWUgYW55d2hlcmUgdnMgJWluJSB3aGljaCBpcyBhbiBleGFjdCBtYXRjaAoKZ3JlcGwoJ2NvbnRhaW4nLCB3b3JkcykKZ3JlcGwoJ2NvbnRhaW4gJywgd29yZHMpCmBgYAoKTm90ZTogWW91IGNhbm5vdCBnaXZlIGEgdmVjdG9yIGEgbnVtYmVyLCBlLmcuLCBpbiBzY29yZXNbMiw0XSwgUiBpcyBsb29raW5nIGZvciB0aGUgc2Vjb25kIHJvdyBhbmQgZm91cnRoIGNvbHVtbjsgd291bGQgbmVlZCB0byB3cml0ZToKYGBge3J9CnNjb3JlcyA8LSBjKEpvaG49MjUsIE1hcmdlPTM0LCBEYW49MjQsIEVtaWx5PTI5KQpzY29yZXNbYygyLDQpXQpgYGAKClJldHVybiBpbmRpdmlkdWFscyB3aXRoIHNjb3JlcyBncmVhdGVyIHRoYW4gMzA6CmBgYHtyfQpzY29yZXNbc2NvcmVzID4gMzBdICAKYGBgCgoKCgoKCgoKCgojIyMjIERBVEEgU1RSVUNUVVJFUyAoY3JlYXRlIGEgbGlzdCwgbWF0cml4LCBkYXRhZnJhbWUsIGlkZW50aWZ5IHZhcmlhYmxlcykKCgpOYW1lIGVhY2ggdmVjdG9yIGluIHRoZSBsaXN0OgpgYGB7cn0KbWFyeV9pbmZvIDwtIGxpc3QoY2xhc3Nlcz1jKCJCaW9sb2d5IiwgIk1hdGgiLCAiTXVzaWMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIlBoeXNpY3MiKSwKICAgICAgICAgICAgICAgICAgZnJpZW5kcz1jKCJKb2huIiwgIkRhbiIsICJFbWlseSIpLAogICAgICAgICAgICAgICAgICBTQVQ9YygxNDUwLDEwMDAsMTYwMCkpCmBgYAoKUmV0dXJuIHRoZSBzZWNvbmQgdmVjdG9yOgpgYGB7cn0KbWFyeV9pbmZvW1syXV0KYGBgCgpSZXR1cm4gdGhlIHZlY3RvciBuYW1lZCAiU0FUIgpgYGB7cn0KbWFyeV9pbmZvJFNBVCAKYGBgCgpSZXR1cm4gdGhlIHNlY29uZCBlbGVtZW50IG9mIGZyaWVuZHMgdmVjdG9yCmBgYHtyfQptYXJ5X2luZm8kZnJpZW5kc1syXQpgYGAKCk1ha2UgYSBsaXN0IHRoYXQgY29udGFpbnMgdGhlIHRocmVlIGRlcGFydG1lbnRzIGF0IEx1c2tpbiBhcyB3ZWxsIGFzIHRoZSBudW1iZXIgb2YgZW5yb2xsZWQgc3R1ZGVudHMgaW4gZWFjaC4gIEFzc3VtZSB0aGUgbnVtYmVycyBhcmUgNjAsIDgwLCBhbmQgMTAwLgpgYGB7cn0KbGlzdDAgPC0gbGlzdChkZXB0cyA9IGMoIlB1YmxpY1BvbGljeSIsICJTb2NpYWxXZWxmYXJlIiwgIlVyYmFuUGxhbm5pbmciKSwgZW5yb2xsZWQgPSBjKDYwIDgwLCAxMDApKQpgYGAKCkZvciB0aGVzZSBuZXh0IHF1ZXN0aW9ucywgdXNlIHRoZSBsaXN0IGNyZWF0ZWQgaW4gdGhlIGJlbG93IGNvZGUuCmBgYHtyfQpsaXN0MSA8LSBsaXN0KG5hbWVzPWMoJ0pvaG4nLCAnS2F0ZScsICdKZXNzaWNhJywgJ0NhcmwnKSwgZ3JhZGVzPWMoODAsODUsNjAsMTAwKSwgc2V4PWMoJ01hbGUnLCAnRmVtYWxlJywgJ0ZlbWFsZScsICdNYWxlJykpCmBgYAoKV2hvIGhhcyBncmFkZXMgZ3JlYXRlciB0aGFuIDgzPwpgYGB7cn0KbGlzdDEkbmFtZXNbbGlzdDEkZ3JhZGVzID4gODNdCmBgYAoKV2hpY2ggcGVvcGxlIGFyZSBmZW1hbGU/CmBgYHtyfQpsaXN0MSRuYW1lc1tsaXN0MSRzZXggPT0gIkZlbWFsZSJdCmBgYAoKQ3JlYXRlIGEgMngzIG1hdHJpeCwgZmlsbGluZyBkb3duIGNvbHVtbnM6CmBgYHtyfQphIDwtIG1hdHJpeCgxOjYsIG5yb3c9MikKYGBgCgpGaWxsIGFjcm9zcyByb3dzOgpgYGB7cn0KYiA8LSBtYXRyaXgoNToxNCwgbnJvdz0yLCBieXJvdz1UUlVFKQpgYGAKClJvdyAyLCBjb2x1bW4gMzoKYGBge3J9CmFbMiwzXQpgYGAKCkFsbCByb3dzLCBjb2x1bW4gMjoKYGBge3J9CmJbLDJdCmBgYAoKQWxsIGNvbHVtbnMsIHJvdyAxOgpgYGB7cn0KYVsxLF0KYGBgCgpPdGhlciB3YXlzIHRvIGNyZWF0ZSBtYXRyaWNlczoKYGBge3J9Cm1hdHJpeChjKDAsMSwxLDEsMCwxLDAsMCwxKSwgbnJvdz0zKQoKbWF0cml4KDA6MSwgbnJvdz0xMCwgbmNvbD0xMCkKYGBgCgpDcmVhdGUgYSBtYXRyaXggb2YgMTAgcm93cyBhbmQgMTAgY29sdW1ucyB3aGVyZSBlYWNoIGVudHJ5IGlzIHRoZSBudW1iZXIgMCBvciAxIChtdWx0aXBsZSBvcHRpb25zKS4KYGBge3J9Cm0xIDwtIG1hdHJpeChyYmlub20oMTAwLCAxLCAwLjUpLCBucm93ID0gMTApCm0gPC0gbWF0cml4KDA6MSwgbnJvdz0xMCwgbmNvbD0xMCkKYGBgCgpTaG93IG1lIHRoZSBmaXJzdCByb3cgb2YgdGhlIG1hdHJpeC4KYGBge3J9Cm0xWzEsIF0KYGBgCgpTaG93IHRoZSBmaXJzdCBzaXggcm93cyBvZiB0aGUgbWF0cml4ICh0d28gb3B0aW9ucykuCmBgYHtyfQpoZWFkKG0xKQptMVsxOjYsIF0KYGBgCgpTaG93IHJvd3MgNCB0aHJvdWdoIDguCmBgYHtyfQptMVs0OjgsIF0KYGBgCgpTaG93IHJvd3MgMywgNSwgNywgYW5kIDkuCmBgYHtyfQptMVtjKDMsIDUsIDcsIDkpLCBdCmBgYAoKU2hvdyB0aGUgdGhpcmQgY29sdW1uIG9mIHRoZSBtYXRyaXguCmBgYHtyfQptMVssIDNdCmBgYAoKU2hvdyB0aGUgdGhpcmQsIGZpZnRoLCBhbmQgc2V2ZW50aCBjb2x1bW5zIG9mIHRoZSBtYXRyaXguCmBgYHtyfQptMVssIGMoMywgNSwgNyldCmBgYAoKQ29udmVydCB0aGUgZ3JhZGVzIGxpc3QgZnJvbSBhYm92ZSBpbnRvIGEgZGF0YSBmcmFtZSAoZm91ciBvcHRpb25zKS4KYGBge3J9CmQxIDwtIGRhdGEuZnJhbWUobGlzdDEpCgphcy5kYXRhLmZyYW1lKGxpc3QxKQoKZDFiIDwtIGRhdGEuZnJhbWUobmFtZXM9YygnSm9obicsICdLYXRlJywgJ0plc3NpY2EnLCAnQ2FybCcpLCBncmFkZXM9Yyg4MCw4NSw2MCwxMDApLCBzZXg9YygnTWFsZScsICdGZW1hbGUnLCAnRmVtYWxlJywgJ01hbGUnKSkKCmRhdGEuZnJhbWUobGlzdDEpCmBgYAoKV2hvIGhhcyBncmFkZXMgZ3JlYXRlciB0aGFuIDkwPwpgYGB7cn0KZDFbZDEkZ3JhZGVzID4gOTAsIF0KYGBgCgpXaGljaCBwZW9wbGUgYXJlIG1hbGU/CmBgYHtyfQpkMVtkMSRzZXggPT0gIk1hbGUiLCBdCmBgYAoKV2hhdCBhcmUgdGhlIGZpcnN0IHNpeCByb3dzIGFuZCBuaW5lIGNvbHVtbnMgaW4gdGhlIFNDQUQgZGF0YT8KYGBge3J9ClNDQURkYXRhWzE6NiwgMTo5XQpgYGAKCldoYXQgYXJlIHRoZSBsYXN0IGZpdmUgcm93cyBpbiB0aGUgU0NBRCBkYXRhICh0d28gb3B0aW9ucyk/CmBgYHtyfQp0YWlsKFNDQURkYXRhLCA1KQpTQ0FEZGF0YVsobnJvdyhTQ0FEZGF0YSktNSk6bnJvdyhTQ0FEZGF0YSksXQpgYGAKClNob3cgcm93cyAxMDAgdG8gMTA3IGluIHRoZSBTQ0FEIGRhdGEuCmBgYHtyfQpTQ0FEZGF0YVsxMDA6MTA3LCBdCmBgYAoKV2hhdCBhcmUgdGhlIG5hbWVzIG9mIHRoZSBjb2x1bW5zIGluIHRoZSBTQ0FEIGRhdGE/CmBgYHtyfQpuYW1lcyhTQ0FEZGF0YSkKYGBgCgpIb3cgbWFueSB0aW1lcyBkb2VzIGVhY2ggY291bnRyeSBhcHBlYXIgaW4gdGhlIGRhdGFzZXQgdXNpbmcgJ3RhYmxlJyBpbiB0aGUgU0NBRCBkYXRhPwpgYGB7cn0KdGFibGUoU0NBRGRhdGEkY291bnRyeW5hbWUpCmBgYAoKSG93IG1hbnkgZXZlbnRzIHN0YXJ0ZWQgaW4gZWFjaCB5ZWFyPwpgYGB7cn0KdGFibGUoU0NBRGRhdGEkc3R5cikKdGFibGUoU0NBRGRhdGEkZXlyKQpgYGAKCldyaXRlIGNvZGUgdG8gc2F2ZSBvbmx5IGV2ZW50cyB0aGF0IHN0YXJ0ZWQgaW4gMjAwNiBhcyBhIG5ldyBvYmplY3QuCmBgYHtyfQpuZXcgPC0gU0NBRGRhdGFbU0NBRGRhdGEkc3R5ciA9PSAyMDA2LCBdCmBgYAoKSG93IG1hbnkgZXZlbnRzIHN0YXJ0ZWQgaW4gMjAwNj8KYGBge3J9Cm5yb3cobmV3KQpgYGAKCk1ha2UgYSB0YWJsZSBzaG93aW5nIHRoZSBudW1iZXIgb2YgZXZlbnRzIHBlciBjb3VudHJ5IHRoYXQgc3RhcnRlZCBpbiAyMDA2LgpgYGB7cn0KdGFibGUobmV3JGNvdW50cnluYW1lKQpgYGAKCkRyb3AgYWxsIGV2ZW50cyB3aGVyZSB0aGUgbnVtYmVyIG9mIHBhcnRpY2lwYW50cyBpcyBgLTk5YC4KYGBge3J9Cm5ldzIgPC0gU0NBRGRhdGFbU0NBRGRhdGEkbnBhcnQgIT0gLTk5LCBdCmBgYAoKQ3JlYXRlIGEgbG9naWNhbCB2ZWN0b3IgYW5kIG51bWVyaWMgdmVjdG9yIG9mIGVxdWFsIGxlbmd0aDoKYGBge3J9Cm15ZGF0YSA8LSBkYXRhLmZyYW1lKGRpYWJldGljID0gYyhUUlVFLCBGQUxTRSwgVFJVRSwgRkFMU0UpLAogICAgICAgICAgICAgICAgICAgICBoZWlnaHQgPSBjKDY1LCA2OSwgNzEsIDczKSwgc2V4PWMoJ01hbGUnLCAnRmVtYWxlJywgJ0ZlbWFsZScsICdNYWxlJyksIG1pbnV0ZXNFeGVyY2lzZSA9IGMoMjAsMzAsMjIsODApKQpgYGAKCklkZW50aWZ5IHJvdyAzLCBjb2x1bW4gMiBoZWlnaHQgb2YgdGhpcmQgbWFsZToKYGBge3J9Cm15ZGF0YVszLDJdCmBgYAoKVXNpbmcgY29sdW1uIG5hbWUsIGlkZW50aWZ5IHRoZSBmaXJzdCB0d28gcm93czoKYGBge3J9Cm15ZGF0YVsxOjIsICJoZWlnaHQiXQpgYGAKCkxpc3QgYWxsIHJvd3Mgb2YgY29sdW1uICJkaWFiZXRpYyI6CmBgYHtyfQpteWRhdGFbLCJkaWFiZXRpYyJdCmBgYAoKU3Vic2V0dGluZyBjcmVhdGVzIGEgbnVtZXJpYyB2ZWN0b3I6CmBgYHtyfQpteWRhdGEkaGVpZ2h0WzI6M10KYGBgCgpOdW1lcmljIHZlY3RvcjoKYGBge3J9Cm15ZGF0YVtbImhlaWdodCJdXQpgYGAKCldoYXQgYXJlIHRoZSBuYW1lcyBvZiBteSB2YXJpYWJsZXM/CmBgYHtyfQpjb2xuYW1lcyhteWRhdGEpCmBgYAoKQXNzaWduIGNvbHVtbiBuYW1lczoKYGBge3J9CmNvbG5hbWVzKG15ZGF0YSkgPC0gYygiRGlhYmV0aWMiLCAiSGVpZ2h0IiwgIlNleCIsICJNaW51dGVzIG9mIEV4ZXJjaXNlIHBlciBXZWVrIikKCiNQcm9mIHJlY3MgYWxwaGFiZXRpemluZwpgYGAKClRvIGNoYW5nZSB0aGUgZmlyc3QgdmFyaWFibGUncyBuYW1lLCBqdXN0IHVzZSBpbmRleGluZzoKYGBge3J9CmNvbG5hbWVzKG15ZGF0YSlbMV0gPC0gIkRpYWJldGVzIgoKI3RvIHZlcmlmeToKY29sbmFtZXMobXlkYXRhKSA9PSAnRGlhYmV0ZXMnCgojSWYgeW91IGRvIG5vdCBub3cgdGhlIGluZGV4IGJ1dCBrbm93IHRoZSBuYW1lLCB5b3UgY2FuIGRvIHRoYXQgdG9vOgpjb2xuYW1lcyhteWRhdGEpW2NvbG5hbWVzKG15ZGF0YSkgPT0gJ0RpYWJldGVzJ10gPC0gJ0RpYWJldGljJyAgCiNeV2hlcmUgdGhlIGNvbHVtbiBuYW1lIGlzICJEaWFiZXRlcyIsIGNoYW5nZSBpdCB0byAiRGlhYmV0aWMiLgpgYGAKCldoYXQgYXJlIHRoZSBkaW1lbnNpb25zIG9mIG15IGRhdGEgZnJhbWU/CmBgYHtyfQpkaW0obXlkYXRhKQpgYGAKCldoYXQgYXJlIHRoZSBmaXJzdCBmaXZlIHJvd3M/CmBgYHtyfQpoZWFkKG15ZGF0YSkKYGBgCgpXaGF0IGFyZSB0aGUgbGFzdCBmaXZlIHJvd3M/CmBgYHtyfQp0YWlsKG15ZGF0YSkKYGBgCgpHZXQgaW5mb3JtYXRpb24gYWJvdXQgdGhlIGRhdGFmcmFtZSwgc3RydWN0dXJlOgpgYGB7cn0Kc3RyKG15ZGF0YSkKYGBgCgpHZXQgc3VtbWFyeSBzdGF0aXN0aWNzOgpgYGB7cn0Kc3VtbWFyeShteWRhdGEpCgpzdW1tYXJ5KG15ZGF0YSRoZWlnaHQpCmBgYAoKR2V0IG51bWJlciBvZiByb3dzOgpgYGB7cn0KbnJvdyhteWRhdGEpCgojRG9uJ3Qgc2VsZWN0IG9uIGNvbCBudW1iZXJzLCB0aGV5IHdvbid0IHN0YXkgdGhlIHNhbWU7IHVzZSBjb2xuYW1lcyhteWRhdGEpW2NvbG5hbWVzKG15ZGF0YSkgPT0gJ0RpYWJldGVzJ10gKGNhbiBhbHNvIHVzZSBncmVwKQpgYGAKCkNyZWF0ZSBhIGRhdGFmcmFtZSB0aGF0IGlzIGZpdmUgcm93cyB0YWxsIGFuZCB0aHJlZSBjb2x1bW5zIHdpZGUuICBUaGUgZmlyc3QgY29sdW1uIGlzIHBlb3BsZSdzIG5hbWVzOiBBbWFuZGEsIEJyYW5kb24sIENhcmwsIERhdmlkLCBhbmQgRWxlYW5vci4gVGhlIHNlY29uZCBjb2x1bW4gaXMgdGhlaXIgYmlydGggeWVhcjogMTk4MSwgMTkyOCwgMTk5NywgMjAwMiwgMTk4OS4gIFRoZSB0aGlyZCBjb2x1bW4gaXMgdGhlaXIgaGVpZ2h0IGluIGNlbnRpbWV0ZXJzOiAyMDAsIDE4MCwgMTc2LCAxOTAsIDIxMC4KCmBgYHtyfQpteWRhdGEgPC0gZGF0YS5mcmFtZShkaWFiZXRpYyA9IGMoVFJVRSwgRkFMU0UsIFRSVUUsIEZBTFNFKSwgCiAgICAgICAgICAgICAgICAgICAgIGhlaWdodCA9IGMoNjUsIDY5LCA3MSwgNzMpLCBzZXg9YygnTWFsZScsICdGZW1hbGUnLCAnRmVtYWxlJywgJ01hbGUnKSwgCiAgICAgICAgICAgICAgICAgICAgIG1pbnV0ZXNFeGVyY2lzZSA9IGMoMjAsMzAsMjIsODApKQoKZDIgPC0gZGF0YS5mcmFtZShuYW1lcz1jKCdBbWFuZGEnLCAnQnJhbmRvbicsICdDYXJsJywgJ0RhdmlkJywgJ0VsZWFub3InKSwgeWVhcj1jKDE5ODEsIDE5MjgsIDE5OTcsIDIwMDIsIDE5ODkpLCBoZWlnaHQgPSBjKDIwMCwxODAsMTc2LDE5MCwyMTApKQpgYGAKClNlbGVjdCByb3dzIHRocmVlIHRocm91Z2ggZml2ZSBvZiB0aGUgZGF0YWZyYW1lLgpgYGB7cn0KZDJbMzo1LF0KYGBgCgpSZW5hbWUgdGhlIG5hbWUgY29sdW1uIHRvICJGaXJzdF9OYW1lIi4gIFJlbmFtZSB0aGUgYmlydGh5ZWFyIGNvbHVtbiB0byAiWWVhciIuCmBgYHtyfQpjb2xuYW1lcyhkMilbY29sbmFtZXMoZDIpPT0nbmFtZXMnXSA8LSAnRmlyc3RfTmFtZScKY29sbmFtZXMoZDIpW2NvbG5hbWVzKGQyKT09J3llYXInXSA8LSAnWWVhcicKYGBgCgpDcmVhdGUgYSBuZXcgZGF0YWZyYW1lIG9mIG9ubHkgdGhlIGluZGl2aWR1YWxzIHdobyBhcmUgYXQgbGVhc3QgMjAwIGNlbnRpbWV0ZXJzIHRhbGwuCmBgYHtyfQpkdGFsbCA8LSBkMltkMiRoZWlnaHQgPiAyMDAsXQpgYGAKCkNyZWF0ZSBhIGZvdXIgcm93LCBmb3VyIGNvbHVtbiBtYXRyaXguICBNdWx0aXBsZSB0aGF0IG1hdHJpeCBieSAzLgpgYGB7cn0KbSA8LSBtYXRyaXgoMTozLCBucm93PTQsIG5jb2w9NCkKbTIgPC0gbWF0cml4KDE6MTYsIG5yb3c9NCkKCm0yKjMKYGBgCgpGcm9tIHRoYXQgbWF0cml4LCB0YWtlIHRoZSBzdW0gb2YgdGhlIGZpcnN0IGNvbHVtbi4gIFRha2UgdGhlIHN1bSBvZiB0aGUgdGhpcmQgcm93LiAKYGBge3J9CnN1bShtMlssMV0pCm0yWywxXQp0ZW1wIDwtIG0yWywxXQpzdW0odGVtcCkKCnN1bShtMlszLF0pCmBgYAoKSG93IG1hbnkgY29sdW1ucz8KbmNvbChkYXRhKQoKSG93IG1hbnkgcm93cz8KbnJvdyhkYXRhKQoKV2hhdCBhcmUgdGhlIGNvbHVtbiBuYW1lcz8KbmFtZXMoZGF0YSkgIAoKV2hhdCBhcmUgdGhlIHJvdyBuYW1lcz8Kcm93bmFtZXMoZGF0YSkKCldoYXQgYXJlIHRoZSBmaXJzdCA2IHJvd3M/CmhlYWQoZGF0YSkKCldoYXQgYXJlIHRoZSBsYXN0IDYgcm93cz8KdGFpbChkYXRhKQoKTm90ZSB0aGF0IHlvdSBjYW4gdGVsbCBoZWFkKCkgYW5kIHRhaWwoKSBob3cgbWFueSByb3dzIHlvdSB3YW50IHRvIHNlZS4gIDUgaXMgdGhlIGRlZmF1bHQuCmhlYWQoZGF0YSwgMTApCmhlYWQoZGF0YSwgMikKCnRhaWwoZGF0YSwgOCkKdGFpbChkYXRhLCAyKQoKSWYgeW91IHdhbnQgdG8ga25vdyB0aGUgc2V0IG9mIHZhbHVlcyBhIHZhcmlhYmxlIHRha2VzLCB1c2UgdW5pcXVlKCkKdW5pcXVlKGRhdGEkQ09VTlRSWSkKdW5pcXVlKGRhdGEkQllFQVIpCgpJZiB5b3Ugd2FudCB0byBnZXQgYSBzZW5zZSBvZiBob3cgbWFueSBvYnNlcnZhdGlvbnMgdGhlcmUgYXJlIGZvciBlYWNoIHZhbHVlIG9mIGEgdmFyaWFibGUsIHVzZSB0YWJsZSgpCnRhYmxlKGRhdGEkQ09VTlRSWSkKdGFibGUoZGF0YSRCWUVBUikKCkV4cGxvcmUgaG93IHRvIGFjY2VzcyBkaWZmZXJlbnQgZGF0YSBwb2ludHMsIGJhc2VkIG9uIHZhcmlhYmxlIHZhbHVlcy4gIFdlIHdpbGwgdGhlbiBtb2RpZnkgc29tZSBvZiB0aGVzZSB2YXJpYWJsZXMgYW5kIHNhdmUgdGhlIHJlc3VsdCB0byBhIG5ldyBmaWxlLgpgYGB7cn0KIyBTZWxlY3QgYnkgeWVhci4gIAp0ZW1wIDwtIGRhdGFbZGF0YSRCWUVBUiA9PSAnMjAxMCcsXSAgCiMgVGhlIGV2ZW50cyBmcm9tIDIwMTAgYXJlIG5vdyBpbiB0aGlzIGRhdGFmcmFtZQpkaW0odGVtcCkKbnJvdyh0ZW1wKSAtIG5yb3coZGF0YSkKCnRlbXAgPC0gZGF0YVtkYXRhJEJZRUFSID09ICcyMDExJyxdICAKIyBOb3csIHRoZSAidGVtcCIgZGF0YWZyYW1lIGlzIGZvciAyMDExLgoKdGVtcCA8LSBkYXRhW2RhdGEkQllFQVIgPCAyMDEwLF0gIAp0ZW1wMiA8LSBkYXRhW2RhdGEkQllFQVIgPD0gMjAxMCxdCgpub2RlYXRocyA8LSBkYXRhW2RhdGEkTkRFQVRIID09IDAsXQpqYW51YXJ5IDwtIGRhdGFbZGF0YSRCTU9OVEggPT0gMSxdCm5vdGphbnVhcnkgPC0gZGF0YVtkYXRhJEJNT05USCAhPSAxLF0gICMgIT0gbWVhbnMgIm5vdCBlcXVhbCB0byIuCm5vdGphbnVhcnkyIDwtIGRhdGFbZGF0YSRCTU9OVEggPiAxLF0KYGBgCgpNYW5pcHVsYXRlIHZhcmlhYmxlcyBhbmQgdGhlbiBzYXZlIHRoZSBkYXRhZnJhbWUuIFJlbWVtYmVyLCBlYWNoIGNvbHVtbiBpcyBqdXN0IGEgdmVjdG9yLCBzbyB0aGUgdmVjdG9yIG9wZXJhdGlvbnMgd2UgbGVhcm5lZCBlYXJsaWVyIGFyZSB3aGF0IHdlIHdpbGwgZG8gaGVyZS4gIE5vdGUgdGhhdCBJIGRvIHNvbWUgdHJhbnNmb3JtYXRpb25zLCBsaWtlIG11bHRpcGxlIGBCREFZYCBieSBgQk1PTlRIYCwganVzdCB0byBkZW1vbnN0cmF0ZSBjYXBhYmlsaXRpZXMuCmBgYHtyfQpkYXRhJG5ld25wYXJ0IDwtIGRhdGEkTlBBUlQqMTAKZGF0YSRuZXducGFydDIgPC0gaWZlbHNlKGRhdGEkTlBBUlQgIT0gOTksIGRhdGEkTlBBUlQsIE5BKQpkYXRhJG5wYXJ0IDwtIGRhdGEkTlBBUlQvMTAwMCAgCm5hbWVzKGRhdGEpICAjIE5vdGljZSB0aGF0IHRoZSBhYm92ZSBsaW5lIGNyZWF0ZWQgYSBuZXcgdmFyaWFibGUuCgpkYXRhJE5QQVJUIDwtIGRhdGEkTlBBUlQvMTAwMCAgCiMgQ3JlYXRpbmcgYSBuZXcgdmFyaWFibGUgd2l0aCB0aGUgc2FtZSBuYW1lIGFzIGFuIGV4aXN0aW5nIG9uZSByZXBsYWNlcyB0aGF0IHZhcmlhYmxlCgpkYXRhJGRlYXRoUmF0ZSA8LSBkYXRhJE5ERUFUSC9kYXRhJE5QQVJUICAjIFdoYXQgZG9lcyB0aGlzIGRvPyAgCnN1bW1hcnkoZGF0YSRkZWF0aFJhdGUpICAjIEl0IGRpZCBub3RoaW5nIGJlY2F1c2UgTkRFQVRIIGlzIHdlaXJkbHkgY29uc3RydWN0ZWQuICAKIyBXZSB3aWxsIG5vdCBkaXNjdXNzIGhlcmUgaG93IHRvIGZpeCBpdC4KCmRhdGEkbm9uc2Vuc2UgPC0gZGF0YSRCREFZKmRhdGEkQk1PTlRICmRhdGEkQ09VTlRSWVtkYXRhJENPVU5UUlkgPT0gJ0NvdGUgZFwnSXZvaXJlJ10gPC0gJ0l2b3J5IENvYXN0JyAgCiMgRmFpbHMgYmVjYXVzZSBSIHRyZWF0cyBjb3VudHJ5IG5hbWUgYXMgYSBmYWN0b3IuICAKCmBgYAoKTG9hZCB0aGUgZGF0YWZyYW1lIGZyb20gZWFybGllci4gIEFkZCB0aGUgYXJndW1lbnQgYHN0cmluZ3NBc0ZhY3RvcnM9RkFMU0UnLgpgYGB7cn0KZGF0YSA8LSByZWFkLmNzdignfi9Eb2N1bWVudHMvVUNMQS9EYXRhL1BSSU9fVXJiYW5Tb2NpYWxEaXNvcmRlci9ldmVudHMuY3N2Jywgc3RyaW5nc0FzRmFjdG9ycyA9IEZBTFNFKQpkYXRhJENPVU5UUllbZGF0YSRDT1VOVFJZID09ICdDb3RlIGRcJ0l2b2lyZSddIDwtICdJdm9yeSBDb2FzdCcgICAKYGBgCgpIb3cgY291bGQgeW91IHZlcmlmeSB0aGF0IHRoZSBjaGFuZ2UgaGFwcGVuZWQ/ICAKYGBge3J9CiMgU2F2ZSB0aGUgZmlsZS4gTWFrZSBzdXJlIHRvIGNoYW5nZSB0aGUgZmlsZSBwYXRoIGZvciB5b3VyIGNvbXB1dGVyLgp3cml0ZS5jc3YoZGF0YSwgJ34vRG9jdW1lbnRzL1VDTEEvRGF0YS9QUklPX1VyYmFuU29jaWFsRGlzb3JkZXIvZXZlbnRzX3YzLmNzdicpCgp3cml0ZS50YWJsZShkYXRhLCAnfi9Eb2N1bWVudHMvVUNMQS9EYXRhL1BSSU9fVXJiYW5Tb2NpYWxEaXNvcmRlci9ldmVudHNfdjMudHh0Jywgc2VwPSdcdCcpIApgYGAKCldoYXQgYXJlIHRoZSBmaXJzdCBmaXZlIHJvd3M/IApgYGB7cn0KaGVhZChtdGNhcnMsIDUpCm10Y2Fyc1sxOjUsXQpgYGAKCldoYXQgYXJlIHRoZSBsYXN0IGZpdmUgcm93cz8KYGBge3J9CnRhaWwobXRjYXJzLCA1KQpgYGAKCldoYXQgYXJlIHJvd3MgMTIgdGhyb3VnaCAyMT8KYGBge3J9Cm10Y2Fyc1sxMjoyMSxdCmBgYAoKV2hhdCBhcmUgdGhlIGNvbHVtbiBuYW1lcz8KYGBge3J9Cm5hbWVzKG10Y2FycykKc29ydChuYW1lcyhtdGNhcnMpKQpgYGAKClVzaW5nIG10Y2FyczogY3JlYXRlIGEgbmV3IHZhcmlhYmxlIHRoYXQgaXMgaG9yc2Vwb3dlciBwZXIgY3lsaW5kZXIuCmBgYHtyfQptdGNhcnMkaHBjIDwtIG10Y2FycyRocC9tdGNhcnMkY3lsCmBgYAoKVXNpbmcgbXRjYXJzOiBXZWlnaHQgaXMgaW4gdGhvdXNhbmRzIG9mIHBvdW5kcy4gQ3JlYXRlIGEgbmV3IHZhcmlhYmxlIHRoYXQgaXMgd2VpZ2h0IGluIHBvdW5kcy4gCmBgYHtyfQptdGNhcnMkd3RfbmV3IDwtIG10Y2FycyR3dCoxMDAwCmBgYAoKVXNpbmcgbXRjYXJzOiBDcmVhdGUgYSBuZXcgZGF0YWZyYW1lIG9mIGNhcnMgdGhhdCBnZXQgYXQgbGVhc3QgMjAgbWlsZXMgcGVyIGdhbGxvbi4gIEhvdyBtYW55IGNhcnMgaXMgdGhhdD8KYGBge3J9Cm5ld19tdGNhcnMgPC0gbXRjYXJzW210Y2FycyRtcGcgPj0gMjAsXQpgYGAKClVzaW5nIG10Y2FyczogQ3JlYXRlIGEgbmV3IGRhdGFmcmFtZSBvZiBjYXJzIHRoYXQgZ2V0IGZld2VyIHRoYW4gMjAgbWlsZXMgcGVyIGdhbGxvbi4KYGBge3J9Cm5ld19tdGNhcnMyIDwtIG10Y2Fyc1ttdGNhcnMkbXBnIDwgMjAsXQpgYGAKCgoKCgoKCgoKCgoKCiMjIyMgUExPVFMgKGhpc3RvZ3JhbXMsIHBsb3RzLCBzYXZlIHBkZikKCk1ha2UgYSBwbG90IG9mIHRoZSBudW1iZXIgb2YgcGFydGljaXBhbnRzIGF0IGVhY2ggZXZlbnQgdXNpbmcgdGhlIFNDQUQgZGF0YS4gTGFiZWwgdGhlIHggYW5kIHkgYXhpcy4KYGBge3J9CnBsb3QoU0NBRGRhdGEkbnBhcnRbU0NBRGRhdGEkbnBhcnQgIT0gLTk5XSwgeGxhYiA9ICJFdmVudCBJbmRleCIsIHlsYWIgPSAiTnVtYmVyIG9mIFBhcnRpY2lwYW50cyIpCmBgYAoKU2F2ZSB0aGF0IHBsb3QKYGBge3J9CnBkZigifi9EZXNrdG9wL1VDTEEvQ291cnNlcy80X0ZhbGwgMjAxOS9QUCAyOTAvQXNzaWdubWVudHMvYXNzbjFwbG90MS5wZGYiKQpwbG90KFNDQURkYXRhJG5wYXJ0W1NDQURkYXRhJG5wYXJ0ICE9IC05OV0sIHhsYWIgPSAiRXZlbnQgSW5kZXgiLCB5bGFiID0gIk51bWJlciBvZiBQYXJ0aWNpcGFudHMiKSBkZXYub2ZmKCkKYGBgCgpNYWtlIGEgaGlzdG9ncmFtIG9mIHRoZSBudW1iZXIgb2YgcGFydGljaXBhbnRzIGF0IGVhY2ggZXZlbnQuIE1ha2Ugc3VyZSB0byBsYWJlbCB0aGUgYXhlcy4KYGBge3J9Cmhpc3QoU0NBRGRhdGEkbnBhcnRbU0NBRGRhdGEkbnBhcnQgIT0gLTk5XSwgeGxhYiA9ICJOdW1iZXIgb2YgUGFydGljaXBhbnRzIiwgbWFpbiA9ICJIaXN0b2dyYW0gb2YgUGFydGljaXBhbnRzIiwgeWxhYiA9ICJQcm9iYWJpbGl0eSIsIGZyZXEgPSBGQUxTRSkKYGBgCgpNYWtlIGEgYmFycGxvdCBvZiB0aGUgbnVtYmVyIG9mIHBhcnRpY2lwYW50cyBwZXIgZXZlbnQuICBIaW50OiB5b3Ugd2lsbCBuZWVkIHRvIHVzZSBgdGFibGVgIGFzIHdlbGwuCmBgYHtyfQpiYXJwbG90KHRhYmxlKFNDQURkYXRhJG5wYXJ0KSwgeGxhYiA9ICJOdW1iZXIgb2YgUGFydGljaXBhbnRzIiwgeWxhYiA9ICJGcmVxdWVuY3kiKQpgYGAKCkxvYWQgZGF0YSwgZXhwbG9yZSBpdC4KYGBge3J9CmQgPC0gcmVhZC5jc3YoImh0dHBzOi8vc3RhdHMuaWRyZS51Y2xhLmVkdS9zdGF0L2RhdGEvaHNicmF3LmNzdiIsIHN0cmluZ3NBc0ZhY3RvcnMgPSBGQUxTRSkKZFsxOjEwLF0gIwoKbWVhbihkJHJlYWQpCm1lZGlhbihkJHJlYWQpCnZhcihkJHJlYWQpCnN1bW1hcnkoZCRyZWFkKQoKIyB0YWJsZSgpIHByb2R1Y2VzIGNvdW50cwp0YWJsZShkJGZlbWFsZSkKdGFibGUoZCRzZXMpCgojIEZvciBwcm9wb3J0aW9ucywgdXNlIG91dHB1dCBvZiB0YWJsZSgpIGFzIGlucHV0IHRvIHByb3AudGFibGUoKQpwcm9wLnRhYmxlKHRhYmxlKGQkZmVtYWxlKSkKcHJvcC50YWJsZSh0YWJsZShkJHNlcykpCgojIFlvdSBjb3VsZCBkbyB0aGlzIHVzaW5nIG9iamVjdHMKdDEgPC0gdGFibGUoZCRmZW1hbGUpCnByb3AudGFibGUodDEpCgojIFR3by13YXkgdGFibGVzCnQxIDwtIHRhYmxlKGQkZmVtYWxlLCBkJHNlcykKdDEKcHJvcC50YWJsZSh0MSkgICMgUGVyY2VudGFnZXMgYnkgY2VsbApwcm9wLnRhYmxlKHQxLCBtYXJnaW49MikgICMgUGVyY2VudGFnZXMgYnkgY29sdW1uCnByb3AudGFibGUodDEsIG1hcmdpbj0xKSAgIyBQZXJjZW50YWdlcyBieSByb3cKYGBgCgpOb3cgcGxvdCBpdDoKYGBge3J9CnBsb3QoZCRyZWFkKSAgIyBFdy4gIEJ5IGRlZmF1bHQsIFIgdXNlcyBvcGVuIGNpcmNsZXMuICAKcGxvdChkJHJlYWQsIHR5cGU9J2wnKSAgIyBUaGUgeC1heGlzIGlzIGp1c3QgdGhlIHJvdyBpbmRleC4KcGxvdChkJHJlYWQsIGQkd3JpdGUsIHR5cGU9J2wnKSAgIyBUaGlzIGxvb2tzIHdlaXJkIGJlY2F1c2UgUiBpcyB0cnlpbmcgdG8gZHJhdyBsaW5lcyAKIyBiZXR3ZWVuIHRoZSBwb2ludHMgaW4gZWFjaCByb3cuCnBsb3QoZCRyZWFkLCBkJHdyaXRlKSAgCnBsb3QoZCRyZWFkLCBkJHdyaXRlLCBwY2g9MjApICAjIEkgZmluZCB0aGUgb3BlbiBjaXJjbGVzIHVuYXBwZWFsaW5nLgoKIyBUaGUgYHBjaGAgYXJndW1lbnQgbGV0cyB5b3UgY2hhbmdlIHRoZSBzdHlsZS4KcGxvdChkJHJlYWQsIGQkd3JpdGUsIHBjaD0xNiwgeGxhYj0nUmVhZGluZyBTY29yZScsIHlsYWI9J1dyaXRpbmcgU2NvcmUnLCBtYWluPSdDb3JyZWxhdGlvbiBvZiBSZWFkaW5nIGFuZCBXcml0aW5nJykKcGxvdChhcy5mYWN0b3IoZCRmZW1hbGUpLCBkJHdyaXRlKSAgCgpoaXN0KGQkcmVhZCwgeGxhYj0nUmVhZGluZyBTY29yZScsIG1haW49JycpICAKYmFycGxvdCh0YWJsZShkJGZlbWFsZSkpICAjIEJhciBjaGFydApiYXJwbG90KHRhYmxlKGRhdGEkQllFQVIpKQojIFNhdmluZy4gIERvIG5vdCBmb3JnZXQgdG8gY2hhbmdlIHRoZSBwYXRoIHRvIHJlZmxlY3QgeW91ciBjb21wdXRlci4KYGBgCgpVc2luZyBtY2FyczogTWFrZSBhIHRhYmxlIG9mIHRoZSBnZWFyIHZhcmlhYmxlLgpgYGB7cn0KdGFibGUobXRjYXJzJGdlYXIpCmBgYAoKVXNpbmcgbWNhcnM6IE1ha2UgYSBwcm9wb3J0aW9uIHRhYmxlIG9mIHRoZSBnZWFyIHZhcmlhYmxlLgpgYGB7cn0KcHJvcC50YWJsZSh0YWJsZShtdGNhcnMkZ2VhcikpCmBgYAoKVXNpbmcgbWNhcnM6IE1ha2UgYSB0d28td2F5IHRhYmxlIG9mIHRoZSBudW1iZXIgb2YgY3lsaW5kZXJzIGFuZCBudW1iZXIgb2YgZ2VhcnMuCmBgYHtyfQp0YWJsZShtdGNhcnMkZ2VhciwgbXRjYXJzJGN5bCkKYGBgCgpVc2luZyBtY2FyczogTWFrZSBhIHR3by13YXkgcHJvcG9ydGlvbiB0YWJsZSBvZiB0aGUgbnVtYmVyIG9mIGN5bGluZGVycyBhbmQgbnVtYmVyIG9mIGdlYXJzLiBNYWtlIG9uZSB0YWJsZSB3aGVyZSB0aGUgY2VsbHMgYWRkIHRvIDEsIGFub3RoZXIgd2hlcmUgZWFjaCBjb2x1bW4gYWRkcyB0byAxLCBhbmQgYW5vdGhlciB3aGVyZSBlYWNoIHJvdyBkb2VzLgpgYGB7cn0KcHJvcC50YWJsZSh0YWJsZShtdGNhcnMkZ2VhciwgbXRjYXJzJGN5bCkpCnByb3AudGFibGUodGFibGUobXRjYXJzJGdlYXIsIG10Y2FycyRjeWwpLCBtYXJnaW49MikKcHJvcC50YWJsZSh0YWJsZShtdGNhcnMkZ2VhciwgbXRjYXJzJGN5bCksIG1hcmdpbj0xKQpgYGAKClVzaW5nIG1jYXJzOiBQbG90IHRoZSBtaWxlcyBwZXIgZ2FsbG9uIGFnYWluc3QgdGhlIG51bWJlciBvZiBjeWxpbmRlcnMuIApgYGB7cn0KcGxvdChtdGNhcnMkY3lsLCBtdGNhcnMkbXBnKQpwbG90KGFzLmZhY3RvcihtdGNhcnMkY3lsKSwgbXRjYXJzJG1wZykgI2JveCBhbmQgd2hpc2tlcnMKYGBgCgpVc2luZyBtY2FyczogTWFrZSBhIGJhcnBsb3Qgb2YgdGhlIGNhcnMgYnkgdGhlIGNhcmIgdmFyaWFibGUuCmBgYHtyfQpiYXJwbG90KHRhYmxlKG10Y2FycyRjYXJiKSkKYGBgCgpVc2luZyBtY2FyczogTWFrZSBhIGhpc3RvZ3JhbSBvZiBtaWxlcyBwZXIgZ2FsbG9uLgpgYGB7cn0KaGlzdChtdGNhcnMkbXBnKQpgYGAKCiNOb24tTmV0d29yayBQbG90cwpgYGB7cn0KYXR0YWNoKG10Y2FycykgICMgVXNlIHRoZSBtdGNhcnMgZGF0YXNldAoKbmFtZXMobXRjYXJzKSAgIyBUaGUgdmFyaWFibGVzCmRpbShtdGNhcnMpICMgUm93cyBhbmQgY29sdW1ucwptdGNhcnNbMTo3LF0gIyBUaGUgZmlyc3Qgc2V2ZW4gcm93cwoKc3VtbWFyeShtdGNhcnMpICMgU3VtbWFyeSBzdGF0aXN0aWNzIG9mIHRoZSB2YXJpYWJsZXMKCiMgV2UgbWF5IHdhbnQgdGhlIGZ1bGwgY2FyIG5hbWUgbGF0ZXIKbXRjYXJzJGNhciA8LSByb3duYW1lcyhtdGNhcnMpICAKCiMgV2UgbWF5IHdhbnQgdGhlIGNhciBicmFuZAptdGNhcnMkYnJhbmQgPC0gc2FwcGx5KG10Y2FycyRjYXIsIGZ1bmN0aW9uKHgpIHN0cnNwbGl0KHgsIHNwbGl0PScgJylbWzFdXVsxXSkgCgojV2hhdCBkb2VzIHRoZSBsYXN0IGxpbmUgb2YgdGhlIGFib3ZlIGNvZGUgY2h1bmsgZG8/ICBJdCB0YWtlcyB0aGUgYG10Y2FycyRjYXJgIHZlY3RvciBhbmQgc3BsaXRzIGVhY2ggb2JzZXJ2YXRpb24gdXNpbmcgYHN0cnNwbGl0YC4gYHN0cnNwbGl0YCByZXR1cm5zIGEgbGlzdCBvZiBsaXN0cywgd2hlcmUgZWFjaCBlbnRyeSBpcyB0aGUgbnVtYmVyIG9mIHdvcmRzIGluIHRoZSBjYXIgdmFyaWFibGUuICBgW1sxXV1gIGFjY2Vzc2VzIHRoZSBsaXN0LCBhbmQgYFsxXWAgcmV0dXJucyB0aGUgZmlyc3QgdmFsdWUgaW4gdGhhdCBsaXN0LiAgVGhlIGZpcnN0IHZhbHVlIGlzIGFsd2F5cyB0aGUgY2FyIGJyYW5kLCB3aGljaCBJIG5vdGljZWQganVzdCBmcm9tIHZpc3VhbGx5IGluc3BlY3RpbmcgdGhlIGRhdGEuICBgc2FwcGx5YCB0aGVuIHBlcmZvcm1zIHRoYXQgb3BlcmF0aW9uIG9uIGVhY2ggZWxlbWVudCBvZiBgbXRjYXJzJGNhcmA7IGl0IGhhcyB0aGUgZnVuY3Rpb25hbGl0eSBvZiBhIGxvb3AsIGluIG9uZSBsaW5lLgpgYGAKCkJhc2UgUGxvdHRpbmcKYGBge3J9CnBsb3QobXRjYXJzJGhwLCBtdGNhcnMkbXBnLCBwY2g9MjAsIHhsYWI9J0hvcnNlcG93ZXInLCB5bGFiPSdNaWxlcyBwZXIgR2FsbG9uJykKCnBsb3QobXRjYXJzJGhwLCBtdGNhcnMkcXNlYywgcGNoPTE2LCB4bGFiPSdIb3JzZXBvd2VyJywgeWxhYj0nUXVhcnRlciBNaWxlIFNwZWVkJykKCiMgUmVtZW1iZXIsIHdlIGNhbiBzYXZlIGEgcGxvdCBpbiB0aGUgZm9sbG93aW5nIHdheS4KcGRmKCdXZWVrNV9ob3JzZXBvd2VyTVBHLnBkZicpCnBsb3QobXRjYXJzJGRpc3AsIG10Y2FycyRtcGcsIHBjaD0xMCwgeGxhYj0nRW5naW5lIFNpemUnLCB5bGFiPSdNaWxlcyBwZXIgR2FsbG9uJykKZGV2Lm9mZigpCmBgYAoKQmFzZSBQbG90dGluZywgd2l0aCBNdWx0aXBsZSBWYXJpYWJsZXM6IEFzIHdlIGxlYXJuZWQgaW4gbGVjdHVyZSwgYSB0d28tZGltZW5zaW9uYWwgcGxvdCBpcyBvZnRlbiB0b28gc2ltcGxpc3RpYyBmb3IgdGhlIHJlbGF0aW9uc2hpcHMgd2Ugd2FudCB0byBpbnNwZWN0LiAgV2Ugd2lsbCBub3cgbGVhcm4gaG93IHRvIGNoYW5nZSB0aGUgc2l6ZSwgc2hhcGUsIGFuZCBjb2xvciBvZiBwb2ludHMuCmBgYHtyfQojIENoYW5nZSB0aGUgc2l6ZSB3aXRoIHRoZSBjZXggYXJndW1lbnQKcGxvdChtdGNhcnMkaHAsIG10Y2FycyRtcGcsIHBjaD0yMCwgeGxhYj0nSG9yc2Vwb3dlcicsIHlsYWI9J01pbGVzIHBlciBHYWxsb24nLCBjZXg9bXRjYXJzJGRpc3ApCgojV2h5IGRvZXMgdGhhdCBjb2RlIGNyZWF0ZSBhIGJsYWNrIHBsb3Q/ICBCZWNhdXNlIGBjZXhgIG11bHRpcGxpZXMgdGhlIGRlZmF1bHQgc2l6ZSBvZiB0aGUgcG9pbnQgYnkgdGhlIGFyZ3VtZW50IHBhc3NlZCB0byBpdC4gIChgY2V4YCBzdGFuZHMgZm9yIGBgY2hhcmFjdGVyIGV4cGFuc2lvbicnKSAgU2luY2UgYG10Y2FycyRkaXNwIGhhcyB2ZXJ5IGxhcmdlIHZhbHVlcywgYFJgIGlzIHNob3dpbmcgdXMgdmVyeSBsYXJnZSBkb3RzOyB0aGV5IGFyZSBzbyBsYXJnZSwgaW4gZmFjdCwgdGhhdCBubyB3aGl0ZSBzcGFjZSByZW1haW5zLiAgCgojVGhlIGNvZGUgYmVsb3cgZml4ZXMgdGhhdCBpc3N1ZS4gIEhvdyB0byBzaXplIHBvaW50cyBpcyBtb3JlIGFydCB0aGFuIHNpemUuICBCZWNhdXNlIHlvdSBkbyBub3Qgd2FudCB0aGUgYGNleGAgbnVtYmVycyB0byBiZSB0b28gbGFyZ2UsIGl0IGlzIGNvbW1vbiB0byB0YWtlIHRoZSBzcXVhcmUgcm9vdCBvciBsb2dhcml0aG0uICBIZXJlLCBzcXVhcmUgcm9vdHMgd2lsbCBzdGlsbCBjcmVhdGUgZG90cyB0aGF0IGFyZSB0b28gbGFyZ2UsIHNvIEkgZGl2aWRlIGJ5IDEwMCBhcyB3ZWxsIGFzIHRha2UgdGhlIGxvZ2FyaXRobS4KYGBgCgpDaGFuZ2UgdGhlIHNpemUgd2l0aCB0aGUgY2V4IGFyZ3VtZW50CmBgYHtyfQpwbG90KG10Y2FycyRocCwgbXRjYXJzJG1wZywgcGNoPTIwLCB4bGFiPSdIb3JzZXBvd2VyJywgeWxhYj0nTWlsZXMgcGVyIEdhbGxvbicsIGNleD1tdGNhcnMkZGlzcC8xMDApCmxlZ2VuZCgndG9wcmlnaHQnLCBsZWdlbmQ9J1NpemVkIGJ5IGRpc3BsYWNlbWVudCcpCmBgYAoKQ2hhbmdlIHRoZSBzaXplIHdpdGggdGhlIGNleCBhcmd1bWVudApgYGB7cn0KcGxvdChtdGNhcnMkaHAsIG10Y2FycyRtcGcsIHBjaD0yMCwgeGxhYj0nSG9yc2Vwb3dlcicsIHlsYWI9J01pbGVzIHBlciBHYWxsb24nLCBjZXg9bG9nKG10Y2FycyRkaXNwLCBiYXNlPTEwKSkKbGVnZW5kKCd0b3ByaWdodCcsIGxlZ2VuZD0nU2l6ZWQgYnkgZGlzcGxhY2VtZW50JykKYGBgCgoqKldoeSB3b3VsZCBhIHNxdWFyZSByb290IHJlc3VsdCBpbiBkb3RzIHRoYXQgYXJlIHRvbyBsYXJnZT8qKiBtdGNhcnMgaXMgdmVyeSBsYXJnZSEgQmVjYXVzZSB5b3UgZG8gbm90IHdhbnQgdGhlIGBjZXhgIG51bWJlcnMgdG8gYmUgdG9vIGxhcmdlLCBpdCBpcyBjb21tb24gdG8gdGFrZSB0aGUgc3F1YXJlIHJvb3Qgb3IgbG9nYXJpdGhtLiAgSGVyZSwgc3F1YXJlIHJvb3RzIHdpbGwgc3RpbGwgY3JlYXRlIGRvdHMgdGhhdCBhcmUgdG9vIGxhcmdlLCBzbyBJIGRpdmlkZSBieSAxMDAgYXMgd2VsbCBhcyB0YWtlIHRoZSBsb2dhcml0aG0uCgoqKldoYXQgZG9lcyBzaXppbmcgdGhlIGRvdHMgYnkgZGlzcGxhY2VtZW50IHJldmVhbCBhYm91dCB0aGUgcmVsYXRpb25zaGlwIG9mIGRpc3BsYWNlbWVudCB3aXRoIGhvcnNlcG93ZXI/ICBXaGF0IGFib3V0IHdpdGggbWlsZXMgcGVyIGdhbGxvbj8qKiBkZWNyZWFzZWQgaG9yc2Vwb3dlciBtYXkgYmUgYXNzb2NpYXRlZCB3aXRoIGluY3JlYXNlZCBtaWxlcyBwZXIgZ2FsbG9uLgoKTm93LCBsZXQgdXMgY2hhbmdlIHRoZSBzaGFwZSBvZiBwb2ludHMuICBUaGUga2V5IGlzIHRvIHVzZSB0aGUgYHBjaGAgYXJndW1lbnQsIHdoaWNoIHN0YW5kcyBmb3IgInBvaW50IGNoYXJhY3RlciIuICBJZiB5b3UgbG9vayBhdCB0aGUgaGVscCBkb2N1bWVudGF0aW9uIC0gYD9wYXJgLCB0aGVuIHNlYXJjaCBmb3IgYHBjaGAgLSB5b3Ugd2lsbCBzZWUgdGhlIHZhcmlvdXMgc2hhcGVzIGJhc2UgUiBoYXMuICBUaGUgY29kZSBiZWxvdyBmaXJzdCBzaG93cyB5b3UgaG93IHRvIGNoYW5nZSB0aGUgc2hhcGUgb2YgZWFjaCBlbnRyeSwgYW5kIGl0IHRoZW4gc2hvd3MgeW91IGhvdyB0byBjcmVhdGUgYSB2YXJpYWJsZSBlbnVtZXJhdGluZyB0aGUgc2hhcGUgYnkgc29tZSBvdGhlciB2YXJpYWJsZS4KCkNoYW5nZSB0aGUgc2hhcGUgd2l0aCB0aGUgcGNoIGFyZ3VtZW50CmBgYHtyfQpwbG90KG10Y2FycyRocCwgbXRjYXJzJG1wZywgeGxhYj0nSG9yc2Vwb3dlcicsIHlsYWI9J01pbGVzIHBlciBHYWxsb24nLCBwY2g9YygxLDIpKQpgYGAKCioqV2h5IGFyZSBzb21lIGRvdHMgY2lyY2xlcyBidXQgb3RoZXJzIHRyaWFuZ2xlcz8qKiB0aGUgMSBhbmQgMiBpbiB0aGUgcGNoIGxpbmUKCkNoYW5nZSB0aGUgc2hhcGUgd2l0aCB0aGUgcGNoIGFyZ3VtZW50CmBgYHtyfQpwbG90KG10Y2FycyRocCwgbXRjYXJzJG1wZywgeGxhYj0nSG9yc2Vwb3dlcicsIHlsYWI9J01pbGVzIHBlciBHYWxsb24nLCBwY2g9bXRjYXJzJGFtKQpgYGAKClRoaXMgdmFyaWFibGUgaXMgY29udmVuaWVudCBiZWNhdXNlIGl0IGlzIGFscmVhZHkgY29kZWQgYXMgYSBudW1iZXIKYGBge3J9CnN1bW1hcnkobXRjYXJzJGFtKSAgCm10Y2FycyRhbQpgYGAKCkNoYW5nZSBzaGFwZSB0byBiZSBNYXpkYSBvciBub3QgTWF6ZGEKYGBge3J9Cm10Y2FycyRtYXpkYSA8LSBpZmVsc2UobXRjYXJzJGJyYW5kID09ICdNYXpkYScsIDUsIDE4KQpwbG90KG10Y2FycyRocCwgbXRjYXJzJG1wZywgeGxhYj0nSG9yc2Vwb3dlcicsIHlsYWI9J01pbGVzIHBlciBHYWxsb24nLCBwY2g9bXRjYXJzJG1hemRhKQpgYGAKCk5vdywgbGV0IHVzIGNvbG9yIHRoZSBwb2ludHMuICBUaGUgYXBwcm9hY2ggaXMgdGhlIHNhbWUgYXMgd2UgaGF2ZSBzZWVuIC0gcGFzcyBSIGEgdmVjdG9yIG9mIGNvbG9ycyBhbmQgUiB3aWxsIG1ha2UgdGhlIHBvaW50cyB0aG9zZSBjb2xvcnMuICBOb3RpY2UgdGhhdCBJIGhhdmUgZG91YmxlIHRoZSBzaXplIG9mIGVhY2ggcG9pbnQgd2l0aCB0aGUgYGNleGAgYXJndW1lbnQ7IEkgZG8gdGhhdCBzaW1wbHkgdG8gbWFrZSB0aGUgY2hhcnQgbW9yZSBsZWdpYmxlLgoKQ2hhbmdlIHRoZSBjb2xvciB3aXRoIHRoZSBjb2wgYXJndW1lbnQKYGBge3J9CnBsb3QobXRjYXJzJGhwLCBtdGNhcnMkbXBnLCB4bGFiPSdIb3JzZXBvd2VyJywgeWxhYj0nTWlsZXMgcGVyIEdhbGxvbicsIHBjaD0yMCwgY29sPSdibHVlJywgY2V4PTIpCgojIyBDaGFuZ2UgdGhlIGNvbG9yIHdpdGggdGhlIGNvbCBhcmd1bWVudApwbG90KG10Y2FycyRocCwgbXRjYXJzJG1wZywgeGxhYj0nSG9yc2Vwb3dlcicsIHlsYWI9J01pbGVzIHBlciBHYWxsb24nLCBwY2g9MjAsIGNvbD1jKCdibHVlJywgJ3B1cnBsZScpLCBjZXg9MikKCiMjIE1ha2UgdGhlIGNvbG9yIGFyZ3VtZW50IG1ha2Ugc2Vuc2UKY29sb3JzIDwtIGlmZWxzZShtdGNhcnMkd3Q8MywgJ2dyZWVuJywgJ3JlZCcpCnBsb3QobXRjYXJzJGhwLCBtdGNhcnMkbXBnLCB4bGFiPSdIb3JzZXBvd2VyJywgeWxhYj0nTWlsZXMgcGVyIEdhbGxvbicsIHBjaD0yMCwgY29sPWNvbG9ycywgY2V4PTIpCmxlZ2VuZCgndG9wcmlnaHQnLCBsZWdlbmQ9J0dyZWVuOiBXZWlnaHQgPCAzLDAwMCBsYnMnKQpgYGAKCioqV2hhdCBkbyB5b3Ugbm90aWNlIGFib3V0IHRoZSByZWxhdGlvbnNoaXAgYmV0d2VlbiBob3JzZXBvd2VyLCBtaWxlcyBwZXIgZ2FsbG9uLCBhbmQgdGhlIHdlaWdodCBvZiBhIGNhcj8qKgoKUGxvdCBxdWFydGVyIG1pbGUgc2Vjb25kIHRpbWUgYWdhaW5zdCBtaWxlcyBwZXIgZ2FsbG9uLiAgQ2hhbmdlIHRoZSBzaXplIGJhc2VkIG9uIHRoZSBjYXIncyB3ZWlnaHQuCmBgYHtyfQpwbG90KG10Y2FycyRxc2VjLCBtdGNhcnMkbXBnLCBwY2g9MTUsIGNleD1tdGNhcnMkd3QpCnBsb3QobXRjYXJzJHFzZWMsIG10Y2FycyRtcGcsIHBjaD0xNSwgY2V4PW10Y2FycyR3dC8yKSAgIy8yIHRvIG1ha2Ugc2l6ZSBhIGJpdCBzbWFsbGVyCmBgYAoKUGxvdCBxdWFydGVyIG1pbGUgc2Vjb25kIHRpbWUgYWdhaW5zdCBtaWxlcyBwZXIgZ2FsbG9uLiAgQ2hhbmdlIHRoZSBzaGFwZSBiYXNlZCBvbiB3aGV0aGVyIGNhciBoYXMgYW4gYXV0b21hdGljIHRyYW5zbWlzc2lvbi4KYGBge3J9CnNoYXBlIDwtIGlmZWxzZShtdGNhcnMkYW0gPT0gMSwgMjAsIDUpCnBsb3QobXRjYXJzJHFzZWMsIG10Y2FycyRtcGcsIHBjaD1zaGFwZSkKYGBgCgpQbG90IHF1YXJ0ZXIgbWlsZSBzZWNvbmQgdGltZSBhZ2FpbnN0IG1pbGVzIHBlciBnYWxsb24uICBDaGFuZ2UgdGhlIGNvbG9yIGJhc2VkIG9uIHdoZXRoZXIgdGhlIGNhciBoYXMgbW9yZSB0aGFuIDQgY3lsaW5kZXJzLgpgYGB7cn0KbmV3Y29sIDwtIGlmZWxzZShtdGNhcnMkY3lsID4gNCwgJ2JsdWUnLCAnb3JhbmdlJykKcGxvdChtdGNhcnMkcXNlYywgbXRjYXJzJG1wZywgY29sPW5ld2NvbCkKYGBgCgpNYWtlIG9uZSBwbG90IHRoYXQgY29tYmluZXMgdGhlIHRocmVlIHBsb3RzIGFib3ZlLiAgVGhhdCBpcywgY2hhbmdlIHRoZSBzaXplLCBzaGFwZSwgYW5kIGNvbG9yIG9mIGVhY2ggcG9pbnQgYmFzZWQgb24gdGhlIGNhcidzIHdlaWdodCwgdHJhbnNtaXNzaW9uLCBhbmQgbnVtYmVyIG9mIGN5bGluZGVycywgcmVzcGVjdGl2ZWx5LgpgYGB7cn0KcGxvdChtdGNhcnMkcXNlYywgbXRjYXJzJG1wZywgY29sPW5ld2NvbCwgcGNoPXNoYXBlLCBjZXg9bXRjYXJzJHd0LzIpCmBgYAoKTW9kaXlpbmcgdGhlIHNpemUsIHNoYXBlLCBhbmQgY29sb3Igb2Ygbm9kZXMgaXMgaWRlbnRpY2FsLiAgRmlyc3QsIGxldCB1cyBleHBsb3JlIHRoZSBrYXJhdGUgY2x1YiBkYXRhc2V0LgpgYGB7cn0KaW5zdGFsbC5wYWNrYWdlcygnaWdyYXBoZGF0YScpCmxpYnJhcnkoaWdyYXBoZGF0YSkgIAoKZGF0YShrYXJhdGUpICAjIExldCB1cyB1c2UgdGhlIGthcmF0ZSBjbHViIGRhdGEuCnZjb3VudChrYXJhdGUpCmVjb3VudChrYXJhdGUpCmhlYWQoZGVncmVlKGthcmF0ZSkpCgojTm93LCBsZXQgdXMgbW9kaWZ5IHRoZSBzaXplLiAgV2UgZG8gdGhhdCBieSBtYWtpbmcgbm9kZSBhdHRyaWJ1dGVzIGFuZCBjYWxsaW5nIHRob3NlIGF0dHJpYnV0ZXMgd2hlbiBwbG90dGluZy4gIFNlcGFyYXRlbHksIG5vdGljZSBob3cgdGhlIGxheW91dCBvZiBlYWNoIGdyYXBoIGNoYW5nZXMsIHdoaWNoIGVtcGhhc2l6ZXMgdGhlIGFyYml0cmFyaW5lc3Mgb2YgKHgseSkgY29vcmRpbmF0ZXMgZm9yIG5ldHdvcmsgZ3JhcGhzLgoKc2V0LnNlZWQoMDEwODIwMTcpCgpzaXplIDwtIDE6dmNvdW50KGthcmF0ZSkKVihrYXJhdGUpJHNpemUgPC0gc3FydCgxOnZjb3VudChrYXJhdGUpKQpWKGthcmF0ZSkkc2l6ZQoKcGxvdChrYXJhdGUsIHZlcnRleC5zaXplPVYoa2FyYXRlKSRzaXplKQpwbG90KGthcmF0ZSwgc2l6ZT1zaXplKQojIFRoZSBsYWJlbHMgZ2V0IGluIHRoZSB3YXkgZm9yIG5vdwpwbG90KGthcmF0ZSwgdmVydGV4LnNpemU9VihrYXJhdGUpJHNpemUsIHZlcnRleC5sYWJlbD0iIikgCgojIFRoZSBzaXplIHZlY3RvciBjYW4gYmUgc2VwYXJhdGUgZnJvbSBub2RlIGF0dHJpYnV0ZXJzCnNpemUgPC0gMTp2Y291bnQoa2FyYXRlKQpwbG90KGthcmF0ZSwgdmVydGV4LnNpemU9c2l6ZSwgdmVydGV4LmxhYmVsPSIiKQoKIyBJdCBpcyBjb21tb24gdG8gc2l6ZSBieSBkZWdyZWUKcGxvdChrYXJhdGUsIHZlcnRleC5zaXplPWRlZ3JlZShrYXJhdGUpLCB2ZXJ0ZXgubGFiZWw9IiIpCgojV2Ugd2lsbCBub3cgbW9kaWZ5IHRoZSBzaGFwZSBhbmQgY29sb3Igb2Ygbm9kZXMuICBTZWUgPGh0dHA6Ly9pZ3JhcGgub3JnL3IvZG9jL3Bsb3QuY29tbW9uLmh0bWw+W2xpbmtdKHRoaXMgZG9jdW1lbnRhdGlvbikgZm9yIHZhbGlkIHNoYXBlcy4gIEkgd2lsbCBmaXJzdCBzaG93IGhvdyB0byBhcmJpdHJhcmlseSBjaGFuZ2UgdGhlIHNoYXBlLCB0aGVuIEkgd2lsbCBzaG93IGhvdyB0byBoYXZlIHRoZSBzaGFwZSByZWZsZWN0IGEgdmFyaWFibGUgeW91IG1heSBjYXJlIGFib3V0LiAgSSB3aWxsIHJhbmRvbWx5IGFzc2lnbiBnZW5kZXIgdG8gZWFjaCBub2RlIGFuZCBjaGFuZ2Ugc2hhcGUgYnkgZ2VuZGVyLgoKVihrYXJhdGUpJGdlbmRlciA8LSBjKCdtYWxlJywnZmVtYWxlJykKc2hhcGUgPC0gaWZlbHNlKFYoa2FyYXRlKSRnZW5kZXIgPT0gJ21hbGUnLCAnc3F1YXJlJywgJ3ZyZWN0YW5nbGUnKQpwZGYoJ3Rlc3QucGRmJykKcGxvdC5pZ3JhcGgoa2FyYXRlLCB2ZXJ0ZXguc2hhcGU9c2hhcGUsIHZlcnRleC5sYWJlbD0iIikKCiMgSSBoYXZlIGNoYW5nZWQgc2hhcGUgZm9yIHRoZSBmdW4gb2YgaXQuCnBsb3Qoa2FyYXRlLCB2ZXJ0ZXguc2hhcGU9J3NxdWFyZScsIHZlcnRleC5sYWJlbD0iIiwgdmVydGV4LnNpemU9MjApICAKCgpZb3UgbWF5IGhhdmUgbm90aWNlZCB0aGF0IHRoZSBub2RlcyBhbHJlYWR5IGhhdmUgY29sb3JzLiAgTGV0IHVzIGZpZ3VyZSBvdXQgd2h5IHRoYXQgaXMuCiMgU28sIHRoZXJlIGlzIGFscmVhZHkgYSBjb2xvciBhdHRyaWJ1dGUuIHBsb3Qgd2lsbCB1c2UgdGhhdCBieSBkZWZhdWx0CmdldC52ZXJ0ZXguYXR0cmlidXRlKGthcmF0ZSkgIAoKIyBMZXQgdXMgY2hhbmdlIHRoZSBjb2xvci4KcGxvdChrYXJhdGUsIHZlcnRleC5jb2xvcj0nYmxhY2snLCB2ZXJ0ZXgubGFiZWw9IiIsIHZlcnRleC5zaXplPTEwKSAgCgpnZW5kZXJDb2xvciA8LSBpZmVsc2UoVihrYXJhdGUpJGdlbmRlcj09J21hbGUnLCAncGluaycsICdibHVlJykKcGxvdChrYXJhdGUsIHZlcnRleC5jb2xvcj1nZW5kZXJDb2xvciwgdmVydGV4LmxhYmVsPSIiLCB2ZXJ0ZXguc2l6ZT0xMCkKCiMgVGhlIGV4cGVyaWVuY2Ugd2l0aCBgY29sb3JgIHNob3dzIGEgc2hvcnRjdXQgdGhhdCBjYW4gc29tZXRpbWVzIGJlIGNvbmZ1c2luZy4gIElmIHlvdSBoYXZlIG5vZGUgYXR0cmlidXRlcyB0aGF0IGFyZSBjYWxsZWQgYGxhYmVsYCwgYGNvbG9yYCwgb3IgYHNpemVgLCBgcGxvdGAgd2lsbCB1c2UgdGhvc2UgdW5sZXNzIG92ZXJyaWRkZW4uCgojIFBsb3QgdGhlIGthcmF0ZSBuZXR3b3JrIHdpdGggb3JhbmdlIHJlY3RhbmdsZXMuCnBsb3Qoa2FyYXRlLCB2ZXJ0ZXguY29sb3I9J29yYW5nZScsIHZlcnRleC5zaXplPTIwLCB2ZXJ0ZXguc2hhcGU9J2NyZWN0YW5nbGUnKQpwbG90KGthcmF0ZSwgdmVydGV4LmNvbG9yPSdvcmFuZ2UnLCB2ZXJ0ZXguc2l6ZT0yMCwgdmVydGV4LnNoYXBlPSdjcmVjdGFuZ2xlJywgdmVydGV4LmxhYmVsPSIiKQpwbG90KGthcmF0ZSwgdmVydGV4LmNvbG9yPSdvcmFuZ2UnLCB2ZXJ0ZXguc2l6ZT0yMCwgdmVydGV4LnNoYXBlPSdjcmVjdGFuZ2xlJywgdmVydGV4LmxhYmVsPSJhcmJpdHJhcnkiKQoKIyBQbG90IHRoZSBrYXJhdGUgbmV0d29yay4gIFNpemUgdGhlIG5vZGVzIGJ5IHRoZWlyIGRlZ3JlZS4gIENvbG9yIHRoZSBub2RlcyBieSB0aGVpciBmYWN0aW9uLiAgR2l2ZSAiTXIgSGkiIGFuZCAiSm9obiBBIiBkaWZmZXJlbnQgc2hhcGVzIGZyb20gZXZlcnlvbmUgZWxzZTsgdGhlcmUgd2lsbCBiZSB0aHJlZSBzaGFwZXMuCm5ld3NoYXBlIDwtIGlmZWxzZShrYXJhdGUkbmFtZSA9PSAnTXIgSGknLCAnY2lyY2xlJywgaWZlbHNlKGthcmF0ZSRuYW1lID09ICJKb2huIEEiLCAnY3JlY3RhbmdsZScsICdwaWUnKSkgICMgV2h5IGRpZCB0aGF0IG5vdCB3b3JrPwpuZXdzaGFwZSA8LSBpZmVsc2UoVihrYXJhdGUpJG5hbWUgPT0gJ01yIEhpJywgJ2NpcmNsZScsIGlmZWxzZShWKGthcmF0ZSkkbmFtZSA9PSAiSm9obiBBIiwgJ2NyZWN0YW5nbGUnLCAncGllJykpICAjIENhbGxlZCB2ZXJ0ZXggYXR0cmlidXRlIGluY29ycmVjdGx5CgojIEJlbG93IGlzIGEgc2Vjb25kIHdheQpuZXdzaGFwZTIgPC0gcmVwKCdwaWUnLCB2Y291bnQoa2FyYXRlKSkKbmV3c2hhcGUyW1Yoa2FyYXRlKSRuYW1lID09ICdNciBIaSddIDwtICdjaXJjbGUnCm5ld3NoYXBlMltWKGthcmF0ZSkkbmFtZSA9PSAnSm9obiBBJ10gPC0gJ2NyZWN0YW5nbGUnCgpwbG90KGthcmF0ZSwgdmVydGV4LnNpemU9ZGVncmVlKGthcmF0ZSksIHZlcnRleC5jb2xvcj1rYXJhdGUkZmFjdGlvbiwgdmVydGV4LnNoYXBlPW5ld3NoYXBlKQoKcGRmKCd0ZXN0Mi5wZGYnKQpwbG90KGthcmF0ZSwgdmVydGV4LnNpemU9ZGVncmVlKGthcmF0ZSksIHZlcnRleC5jb2xvcj1rYXJhdGUkZmFjdGlvbiwgdmVydGV4LnNoYXBlPW5ld3NoYXBlLCB2ZXJ0ZXgubGFiZWw9IiIpCmRldi5vZmYoKQpgYGAKCgoKCgoKCgoKCgoKCgojIE5FVFdPUksgTEFZT1VUCgoKRmluYWxseSwgbGV0IHVzIGxvb2sgYXQgaG93IG91ciBuZXR3b3JrcyBsb29rIGRpZmZlcmVudCBkZXBlbmRpbmcgb24gdGhlIGxheW91dCB3ZSB1c2UuICBGcmFua2x5LCBgaWdyYXBoYCBpcyB2ZXJ5IGZydXN0cmF0aW5nIGhlcmUsIGFzIHRoZSBhcmd1bWVudHMgdG8gbGF5b3V0IGRvIG5vdCBwb3NzZXNzIHRoZSBzYW1lIHN0cnVjdHVyZS4gIElmIHlvdSB3YW50IHRvIHRyeSBhIGRpZmZlcmVudCBsYXlvdXQsIEkgc3VnZ2VzdCBnb29nbGluZy4gCmBgYHtyfQpwbG90KGthcmF0ZSwgbGF5b3V0PWxheW91dF9yYW5kb21seSwgdmVydGV4LmxhYmVsPSIiLCBtYWluPSJSYW5kb20iLCB2ZXJ0ZXguY29sb3I9J2JsYWNrJywgdmVydGV4LnNpemU9MTApCgpwbG90KGthcmF0ZSwgbGF5b3V0PWxheW91dF9pbl9jaXJjbGUsIHZlcnRleC5sYWJlbD0iIiwgbWFpbj0iUmluZyIsIHZlcnRleC5jb2xvcj0nYmxhY2snLCB2ZXJ0ZXguc2l6ZT0xMCkKCnBsb3Qoa2FyYXRlLCBsYXlvdXQ9bGF5b3V0X2FzX3RyZWUsIHZlcnRleC5sYWJlbD0iIiwgbWFpbj0iVHJlZSIsIHZlcnRleC5jb2xvcj0nYmxhY2snLCB2ZXJ0ZXguc2l6ZT0xMCkKCnBsb3Qoa2FyYXRlLCBsYXlvdXQ9bGF5b3V0X2FzX3RyZWUoa2FyYXRlLCBjaXJjdWxhcj1UUlVFKSwgdmVydGV4LmxhYmVsPSIiLCBtYWluPSJDaXJjdWxhciBUcmVlIiwgdmVydGV4LmNvbG9yPSdibGFjaycsIHZlcnRleC5zaXplPTEwKQoKcGxvdChrYXJhdGUsIGxheW91dD1sYXlvdXQuZnJ1Y2h0ZXJtYW4ucmVpbmdvbGQoa2FyYXRlKSwgdmVydGV4LmxhYmVsPSIiLCBtYWluPSJGcnVjaHRlcm1hbi1SZWluZ29sZCIsIHZlcnRleC5jb2xvcj0nYmxhY2snLCB2ZXJ0ZXguc2l6ZT0xMCkKYGBgCgoKQmVsb3cgaXMgYW5vdGhlciB3YXkgdG8gZ2VuZXJhdGUgY29vcmRpbmF0ZXMuICBZb3UgZ2VuZXJhdGUgdGhlIGNvb3JkaW5hdGVzIGJlZm9yZSBjYWxsaW5nIHBsb3QsIHNhdmUgdGhhdCBhcyBhbiBvYmplY3QsIHRoZW4gcGFzcyB0aGF0IG9iamVjdCB0byBwbG90LiAgVGhpcyBhcHByb2FjaCBpcyB3aGF0IHlvdSB3b3VsZCBkbyBpZiB5b3Ugd2FudCB0byBtYW51YWxseSBjaGFuZ2UgY29vcmRpbmF0ZXMgdGhhdCBgaWdyYXBoYCBnZW5lcmF0ZXMuICBZb3UgY2FuIGFsc28gdXNlIHRoaXMgYXBwcm9hY2ggdG8gZ2VuZXJhdGUgeW91ciBvd24gbGF5b3V0LgpgYGB7cn0KY29vcmRpbmF0ZXMgPC0gbGF5b3V0LnJhbmRvbShrYXJhdGUpCnBsb3Qoa2FyYXRlLCBsYXlvdXQ9Y29vcmRpbmF0ZXMsIHZlcnRleC5sYWJlbD0iIiwgbWFpbj0iUmFuZG9tIiwgdmVydGV4LmNvbG9yPSdibGFjaycsIHZlcnRleC5zaXplPTEwKQoKY29vcmRpbmF0ZXMgPC0gbGF5b3V0LmNpcmNsZShrYXJhdGUpCnBsb3Qoa2FyYXRlLCBsYXlvdXQ9Y29vcmRpbmF0ZXMsIHZlcnRleC5sYWJlbD0iIiwgbWFpbj0iUmluZyIsIHZlcnRleC5jb2xvcj0nYmxhY2snLCB2ZXJ0ZXguc2l6ZT0xMCkKCiMjIExldCB1cyBmaWd1cmUgb3V0IGhvdyB0byBtYW51YWxseSBjaGFuZ2UgY29vcmRpbmF0ZXMuICAKY29vcmRpbmF0ZXMgICNPaywgZWFjaCByb3cgaXMgKHgseSkgY29vcmRpbmF0ZXMKCiMgQ29tcHJlc3MgdGhlIHRvcHMgYW5kIGJvdHRvbXMKY29vcmRpbmF0ZXNbLDJdW2Nvb3JkaW5hdGVzWywyXSA+IC44XSA8LSAuOApjb29yZGluYXRlc1ssMl1bY29vcmRpbmF0ZXNbLDJdIDwgLS44XSA8LSAtLjgKCnBsb3Qoa2FyYXRlLCBsYXlvdXQ9Y29vcmRpbmF0ZXMsIHZlcnRleC5sYWJlbD0iIiwgbWFpbj0iQWRqdXN0ZWQgUmluZyIsIHZlcnRleC5jb2xvcj0nYmxhY2snLCB2ZXJ0ZXguc2l6ZT0xMCkKCiMgTGV0J3MgcHVsbCBvdXQgdGhlIHR3byBub2RlcyB3aXRoIG5hbWVzCmdldC52ZXJ0ZXguYXR0cmlidXRlKGthcmF0ZSkKY29vcmRpbmF0ZXMgPC0gbGF5b3V0LmNpcmNsZShrYXJhdGUpCgpjb29yZGluYXRlc1tncmVwKCdNciBIaScsIGdldC52ZXJ0ZXguYXR0cmlidXRlKGthcmF0ZSkkbmFtZSksXSA8LSBjKDEsMSkKY29vcmRpbmF0ZXNbZ3JlcCgnSm9obiBBJywgZ2V0LnZlcnRleC5hdHRyaWJ1dGUoa2FyYXRlKSRuYW1lKSxdIDwtIGMoLTEsLTEpCgpwbG90KGthcmF0ZSwgbGF5b3V0PWNvb3JkaW5hdGVzLCB2ZXJ0ZXgubGFiZWw9IiIsIG1haW49IkFkanVzdGVkIFJpbmciLCB2ZXJ0ZXguY29sb3I9J2JsYWNrJywgdmVydGV4LnNpemU9MTApCgojIE1ha2UgeW91ciBvd24gY29vcmRpbmF0ZXMKeCA8LSBzZXEoLTEsIDEsIGxlbmd0aD12Y291bnQoa2FyYXRlKSkKeSA8LSBzZXEoMSwgLTEsIGxlbmd0aD12Y291bnQoa2FyYXRlKSkKCmNvb3JkaW5hdGVzIDwtIG1hdHJpeChjKHgseSksbmNvbD0yKQogICAgICAgICAgICAgICAgICAgICAgCnBsb3Qoa2FyYXRlLCBsYXlvdXQ9Y29vcmRpbmF0ZXMsIG1haW49IlVzZXIgU3VwcGxpZWQgQ29vcmRpbmF0ZXMiKQoKCiNUaGUgZm9sbG93aW5nIHF1ZXN0aW9ucyB1c2UgdGhlIGBVS2ZhY3VsdHlgIGRhdGEuICBHbyBhaGVhZCBhbmQgbWFrZSBpdCBhbiB1bmRpcmVjdGVkIGdyYXBoIGZpcnN0LCB1c2luZyBgYXMudW5kaXJlY3RlZGAgd2l0aCBgbW9kZT1jKCdjb2xsYXBzZScpYC4KCgojIExvYWQgdGhlIFVLZmFjdWx0eSBkYXRhIGFuZCBjb252ZXJ0IGl0IHRvIGFuIHVuZGlyZWN0ZWQgZ3JhcGguCmRhdGEoVUtmYWN1bHR5KQpnIDwtIGFzLnVuZGlyZWN0ZWQoVUtmYWN1bHR5LCBtb2RlPSJjb2xsYXBzZSIpCgojIFdoYXQgYXJlIHRoZSB2ZXJ0ZXggYXR0cmlidXRlcz8KZ2V0LnZlcnRleC5hdHRyaWJ1dGUoZykKCiMgUGxvdCB0aGUgZ3JhcGggdXNpbmcgYSByYW5kb20gbGF5b3V0LgpwbG90KGcsIGxheW91dD1sYXlvdXRfcmFuZG9tbHkpCgojIFBsb3QgdGhlIGdyYXBoIHVzaW5nIGEgY2lyY2xlIGxheW91dC4KcGxvdChnLCBsYXlvdXQ9bGF5b3V0X2luX2NpcmNsZSkKCiMgUGxvdCB0aGUgZ3JhcGggdXNpbmcgYSB0cmVlIGxheW91dC4KcGxvdChnLCBsYXlvdXQ9bGF5b3V0X2FzX3RyZWUpCgojIFBsb3QgdGhlIGdyYXBoIHVzaW5nIGEgRnJ1Y2h0ZXJtYW4tUmVpbmdvbGQgbGF5b3V0LgpwbG90KGcsIGxheW91dD1sYXlvdXRfcmFuZG9tbHkpCgojIENvbG9yIHRoZSBub2RlcyBhY2NvcmRpbmcgdG8gdGhlaXIgZ3JvdXAuICBDaGFuZ2UgdGhlIG5vZGUgc2l6ZSBiYXNlZCBvbiB0aGUgbm9kZSBkZWdyZWUuICBSYW5kb21seSBkZXRlcm1pbmUgaWYgYSBub2RlIGlzIGEgbWFuIG9yIHdvbWFuIGFuZCBjaGFuZ2UgaXRzIHNoYXBlIGFjY29yZGluZ2x5LiAgVXNlIHRoZSBGcnVjaHRlcm1hbi1SZWluZ29sZCBsYXlvdXQuIFNhdmUgdGhlIGZpbGUgYXMgYSAucGRmLgoKVihnKSRnZW5kZXIgPC0gYygnbWFsZScsICdmZW1hbGUnKQpWKGcpJHNoYXBlIDwtIGlmZWxzZShWKGcpJGdlbmRlciA9PSAnbWFsZScsICdwaWUnLCAnY3JlY3RhbmdsZScpCgpwZGYoJ2ZpbmFscGxvdC5wZGYnKQpwbG90KGcsIHZlcnRleC5jb2xvcj1WKGcpJEdyb3VwLCB2ZXJ0ZXguc2l6ZT1kZWdyZWUoZyksIGxheW91dD1sYXlvdXQuZnJ1Y2h0ZXJtYW4ucmVpbmdvbGQoZyksIHZlcnRleC5sYWJlbD0iIikgIAojIHNoYXBlIGlzIHBsb3R0ZWQgYmVjYXVzZSBpZ3JhcGggcmVjb2duaXplcyB0aGUgc2hhcGUgYXR0cmlidXRlCmRldi5vZmYoKQpgYGAKCgoKCgoKCgoKCgoKCgoKCiMjIyMgQ0VOVFJBTElUWQoKRGVncmVlIENlbnRyYWxpdHk6IExldCB1cyB1c2UgYSBkaXJlY3RlZCBkYXRhc2V0LCBgVUtmYWN1bHR5YCB0byBleHBsb3JlIGRpZmZlcmVudCBtZWFzdXJlcyBvZiBkZWdyZWUgY2VudHJhbGl0eS4KYGBge3J9CmxpYnJhcnkoaWdyYXBoZGF0YSkKbGlicmFyeShpZ3JhcGgpCgojIExvYWQgZGF0YQpkYXRhKFVLZmFjdWx0eSkKCiMgRXhwbG9yZSBub2RlIGF0dHJpYnV0ZXMKZ2V0LnZlcnRleC5hdHRyaWJ1dGUoVUtmYWN1bHR5KQoKIyBNYWtlIHZlY3RvcnMgb2YgZGVncmVlcwppbmRlZ3JlZSA8LSBkZWdyZWUoVUtmYWN1bHR5LCBtb2RlPWMoJ2luJykpCm91dGRlZ3JlZSA8LSBkZWdyZWUoVUtmYWN1bHR5LCBtb2RlPWMoJ291dCcpKQpkZWdyZWVDIDwtIGluZGVncmVlK291dGRlZ3JlZQpkZWdyZWVDZW50cmFsaXR5IDwtIGRlZ3JlZShVS2ZhY3VsdHksIG1vZGU9YygndG90YWwnKSkKZEMyIDwtIGRlZ3JlZShVS2ZhY3VsdHksIG1vZGU9YygnYWxsJykpCgojIFRoZSBhcmd1bWVudCB0b3RhbCwgYWxsIGlzIHRoZSBzYW1lCmRlZ3JlZUNlbnRyYWxpdHkgPT0gZEMyCmBgYAoKTm93LCBsZXQgdXMgc2l6ZSBub2RlcyBieSB0aGVpciBjZW50cmFsaXR5OgpgYGB7cn0Kc2V0LnNlZWQoMDEwODIwMTgpCgojIFNhdmluZyB0aGUgbGF5b3V0IGFzIGFuIG9iamVjdCBlbnN1cmVzIHRoYXQgdGhlIGdyYXBocyB3aWxsIGhhdmUgdGhlIG5vZGVzIGluIHRoZSBzYW1lIHBsYWNlLgojIFdpdGggYSBmb3JjZS1kaXJlY3RlZCBsYXlvdXQsIHlvdSBjb3VsZCBnZXQgdmVyeSBkaWZmZXJlbnQgcmVzdWx0cyBpZiB0aGUgbWVhc3VyZXMgb2YgY2VudHJhbGl0eSBkaWZmZXIgbm90aWNhYmx5LiAgCmwgPC0gbGF5b3V0LmZydWNodGVybWFuLnJlaW5nb2xkKFVLZmFjdWx0eSkKcGxvdChVS2ZhY3VsdHksIHZlcnRleC5zaXplPWluZGVncmVlLCBtYWluPSJTaXplIGJ5IEluZGVncmVlIiwgbGF5b3V0PWwpCnBsb3QoVUtmYWN1bHR5LCB2ZXJ0ZXguc2l6ZT1vdXRkZWdyZWUsIG1haW49IlNpemUgYnkgT3V0ZGVncmVlIiwgbGF5b3V0PWwpCnBsb3QoVUtmYWN1bHR5LCB2ZXJ0ZXguc2l6ZT1kZWdyZWVDZW50cmFsaXR5LCBtYWluPSJTaXplIGJ5IERlZ3JlZSIsIGxheW91dD1sKQoKI1lvdSBtYXkgaGF2ZSBub3RpY2VkIHRoYXQgdGhlcmUgYXJlIGFycm93aGVhZHMgb24gdGhlIGVkZ2VzIG5vdy4gIFRoYXQgaXMgYmVjYXVzZSB0aGUgZ3JhcGggaXMgZGlyZWN0ZWQsIHNvIHRoZSBhcnJvdyBwb2ludHMgaW4gdGhlIGRpcmVjdGlvbiBvZiB0aGUgcmVsYXRpb25zaGlwLiAgV2UgY2FuIGNoYW5nZSBhcnJvdyBhdHRyaWJ1dGVzIG9yIG5vdCBwcmludCB0aGUgYXJyb3doZWFkcy4gIEZvciBtb3JlIGRldGFpbCwgdXNlIGA/cGxvdC5pZ3JhcGhgLiAgSSB3aWxsIGFsc28gc2hvdyBob3cgdG8gY2hhbmdlIHRoZSBlZGdlIGNvbG9yIG9yIHR5cGUuICBSZW1lbWJlciwgeW91IGNhbiB2YXJ5IGFsbCBvZiB0aGVzZSBwYXJhbWV0ZXJzIGJ5IG5vZGUgYnkgY3JlYXRpbmcgYSB2ZWN0b3Igd2l0aCBkaWZmZXJlbnQgdmFsdWVzIGZvciBlZGdlIHR5cGUgb3IgY29sb3I7IEkgd2lsbCBzaG93IG9ubHkgaG93IHRvIG1ha2UgdGhlbSBhbGwgdGhlIHNhbWUgdHlwZSBvciBjb2xvci4KCiMgTm8gYXJyb3cKcGxvdChVS2ZhY3VsdHksIHZlcnRleC5zaXplPWluZGVncmVlLCBtYWluPSJObyBBcnJvdyIsIGVkZ2UuYXJyb3cud2lkdGg9MCkKCiMgU21hbGwgYXJyb3cKcGxvdChVS2ZhY3VsdHksIHZlcnRleC5zaXplPWluZGVncmVlLCBtYWluPSJTbWFsbCBBcnJvdyIsIGxheW91dD1sLCBlZGdlLmFycm93LndpZHRoPS4yNSkKCiMgRG90dGVkIGxpbmUKcGxvdChVS2ZhY3VsdHksIHZlcnRleC5zaXplPWluZGVncmVlLCBtYWluPSJEb3R0ZWQgRWRnZXMiLCBsYXlvdXQ9bCwgZWRnZS5hcnJvdy53aWR0aD0wLCBlZGdlLmx0eT0zKQoKIyBDb2xvcmVkIGVkZ2VzCnBsb3QoVUtmYWN1bHR5LCB2ZXJ0ZXguc2l6ZT1pbmRlZ3JlZSwgbWFpbj0iQ29sb3JlZCBFZGdlcyIsIGxheW91dD1sLCBlZGdlLmFycm93LndpZHRoPTAsIGVkZ2UuY29sb3I9J3JlZCcpCmBgYAoKSXQgaXMgb2Z0ZW4gZGlmZmljdWx0IHRvIHNlZSBob3cgZGlmZmVyZW50IGNlbnRyYWxpdHkgbWVhc3VyZXMgY292YXJ5LCBzaW5jZSBpdCBpcyB1bmNsZWFyIHdoYXQgdGhlIGV4YWN0IG51bWJlciBmb3IgZWFjaCBzaXplIGlzLiAgSXQgaXMgdGhlcmVmb3JlIGEgZ29vZCBpZGVhIHRvIHZpc3VhbGl6ZSB0aGUgbmV0d29yayBpbiBvdGhlciB3YXlzLgpgYGB7cn0Kc2V0LnNlZWQoMDEwODIwMTgpCnBsb3QoaW5kZWdyZWUsIG91dGRlZ3JlZSwgeGxhYj0nSW5kZWdyZWUnLCB5bGFiPSdPdXRkZWdyZWUnLCBwY2g9MjApCiMgTGV0IHVzIHJlcGxhY2UgdGhlIGRvdHMgd2l0aCB2ZXJ0ZXggbGFiZWxzLCBtYWtpbmcgaXQgZWFzaWVyIHRvIHNlZSB3aG8gaXMgd2hpY2ggcG9pbnQuCiMgV2UgZG8gbm90IGhhdmUgdHJ1ZSB2ZXJ0ZXggbGFiZWxzLCBzbyBJIGhhdmUganVzdCB1c2VkIHRoZWlyIG5hbWVzLiAgVGhlIGtleSBpcyB0aGUgdmVjdG9yIHBhc3NlZCB0byB0aGUgbGFiZWxzIGFyZ3VtZW50IG9mIHRleHQoKS4KcGxvdChpbmRlZ3JlZSwgb3V0ZGVncmVlLCB4bGFiPSdJbmRlZ3JlZScsIHlsYWI9J091dGRlZ3JlZScsIHBjaD0nJykKdGV4dCh4PWluZGVncmVlLCB5PW91dGRlZ3JlZSwgbGFiZWxzPVYoVUtmYWN1bHR5KSkKYGBgCgpXZSB3aWxsIHVzZSB0aGUgYFVTYWlycG9ydHNgIGRhdGEgZGF0YXNldCwgd2hpY2ggcmVwcmVzZW50cyBmbGlnaHRzIGluIERlY2VtYmVyIDIwMTAuICBFYWNoIGVkZ2UgaXMgYW4gYWlybGluZSByb3V0ZSBiZXR3ZWVuIHR3byBhaXJwb3J0cyB0aGF0IHVzZXMgdGhlIHNhbWUgYWlyY3JhZnQgdHlwZTsgdGhlcmUgY2FuIGJlIG11bHRpcGxlIGVkZ2VzIGJldHdlZW4gZWFjaCBub2RlLiAgWW91IHdpbGwgbm90aWNlIHRoYXQgdGhlIGVkZ2VzIGhhdmUgYXR0cmlidXRlcyBhcyB3ZWxsLgpgYGB7cn0KIyBMb2FkIHRoZSBVU2FpcnBvcnRzIGRhdGEKZGF0YShVU2FpcnBvcnRzKQoKIyBIb3cgbWFueSBhaXJwb3J0cyBhcmUgaW4gdGhpcyBkYXRhPwp2Y291bnQoVVNhaXJwb3J0cykKCiMgSG93IG1hbnkgYWlyY3JhZnQtdHlwZS1yb3V0ZXMgYXJlIGluIHRoaXMgZGF0YT8KZWNvdW50KFVTYWlycG9ydHMpCgojIE1ha2UgYSBuZXR3b3JrIHBsb3QgdXNpbmcgYSByaW5nIGxheW91dC4KciA8LSBsYXlvdXRfaW5fY2lyY2xlKFVTYWlycG9ydHMpCnBsb3QoVVNhaXJwb3J0cywgbGF5b3V0PXIpCgojIE1ha2UgYSBuZXR3b3JrIHBsb3QgdXNpbmcgYSBGcnVjaHRlcm1hbi1SZWluZ29sZCBsYXlvdXQsIHNpemVkIGJ5IHRoZSB0b3RhbCBkZWdyZWUgb2YgZWFjaCBub2RlLiAgRG8gbm90IGxhYmVsIG5vZGVzLgpmIDwtIGxheW91dC5mcnVjaHRlcm1hbi5yZWluZ29sZChVU2FpcnBvcnRzKQpwbG90KFVTYWlycG9ydHMsIGxheW91dD1mKQoKIyBQbG90IHRoZSBjb3JyZWxhdGlvbiBiZXR3ZWVuIGluZGVncmVlIGFuZCBvdXRkZWdyZWUgZm9yIGVhY2ggbmV0d29yay4gIAppbmRlZ3JlZSA8LSBkZWdyZWUoVVNhaXJwb3J0cywgbW9kZT1jKCdpbicpKQpvdXRkZWdyZWUgPC0gZGVncmVlKFVTYWlycG9ydHMsIG1vZGU9Yygnb3V0JykpCgpwbG90KGluZGVncmVlLCBvdXRkZWdyZWUsIHBjaD0yMCwgeGxhYj0nSW5kZWdyZWUnLCB5bGFiPSdPdXRkZWdyZWUnKQoKIyBSZXBlYXQgdGhlIHByZXZpb3VzIHN0ZXAsIGxhYmVsaW5nIGVhY2ggcG9pbnQgd2l0aCBpdHMgdmVydGV4IG5hbWUuICBZb3UgbWF5IG5lZWQgdG8gbWFrZSB0aGUgc2l6ZSBvZiB0aGUgdGV4dCBzbWFsbGVyIHVzaW5nIHRoZSBjZXggYXJndW1lbnQgaW4gdGV4dCgpLgpwbG90KGluZGVncmVlLCBvdXRkZWdyZWUsIHBjaD0nJywgeGxhYj0nSW5kZWdyZWUnLCB5bGFiPSdPdXRkZWdyZWUnKQp0ZXh0KHg9aW5kZWdyZWUsIHk9b3V0ZGVncmVlLCBsYWJlbD1WKFVTYWlycG9ydHMpJG5hbWUsIGNleD0uNSkKCiMgTGV0J3MgYWRkIHNvbWUgaml0dGVyaW5nIHRvIG1ha2UgZWFzaWVyIHRvIHJlYWQKaTIgPC0gaW5kZWdyZWUrcm5vcm0obj1sZW5ndGgoaW5kZWdyZWUpLCBtZWFuPTAsIHNkPTIwKQpvMiA8LSBvdXRkZWdyZWUgKyBybm9ybShuPWxlbmd0aChvdXRkZWdyZWUpLCBtZWFuPTAsIHNkPTIwKQoKcGxvdChpMiwgbzIsIHBjaD0nJywgeGxhYj0nSW5kZWdyZWUnLCB5bGFiPSdPdXRkZWdyZWUnKQp0ZXh0KHg9aTIsIHk9bzIsIGxhYmVsPVYoVVNhaXJwb3J0cykkbmFtZSwgY2V4PS41KQpgYGAKCiMgTmVpZ2hib3IgQ3VtdWxhdGl2ZSBEZWdyZWUgQ2VudHJhbGl0eQpJbiB0aGlzIHNlY3Rpb24sIHdlIHdpbGwgY2FsY3VsYXRlIG5laWdoYm9yIGN1bXVsYXRpdmUgZGVncmVlIGNlbnRyYWxpdHkuICBUaGVyZSBpcyBubyBkZWZhdWx0IGZ1bmN0aW9uIGluIGBpZ3JhcGhgIHRvIGRvIHRoaXMsIHNvIHdlIHdpbGwgaGF2ZSB0byBkbyBpdCBtYW51YWxseS4gIFdlIHdpbGwgbWFrZSBhIGRpcmVjdGVkIG5ldHdvcmsgdW5kaXJlY3RlZCBhbmQgdGhlbiBsb29wIG92ZXIgZXZlcnkgbm9kZS4KYGBge3J9CiMgTWFrZSB0aGUgZ3JhcGggdW5kaXJlY3RlZApnIDwtIGFzLnVuZGlyZWN0ZWQoVUtmYWN1bHR5LCBtb2RlPSJjb2xsYXBzZSIpCgojIEZvciBlYWNoIG5vZGUsIGdldCBpdHMgbmVpZ2hib3JzLgojIEZvciBlYWNoIG5laWdoYm9yLCBnZXQgaXMgZGVncmVlLgojIFN1bSB0aGUgZGVncmVlIG9mIGVhY2ggbmVpZ2hib3IuCiMgU2V0IHRoYXQgYXMgdGhlIHZlcnRleCBhdHRyaWJ1dGUuClYoZykkbmNkYyA8LSBOQQpmb3IoaSBpbiAxOnZjb3VudChnKSl7CiAgdG90YWwgPC0gMAogIG4gPC0gbmVpZ2hib3JzKGcsIFYoZylbaV0pCiAgZm9yKGogaW4gMTpsZW5ndGgobikpewogICAgZCA8LSBkZWdyZWUoZywgdj1uW2pdKQogICAgdG90YWwgPC0gdG90YWwrZAogIH0KICBWKGcpJG5jZGNbaV0gPC0gdG90YWwKfQoKIyMgSW4gZmFjdCwgdGhlcmUgaXMgYW4gZWFzaWVyIHdheS4KIyBHZXQgYWRqYWNlbmN5IG1hdHJpeAphIDwtIG1hdHJpeChnZXQuYWRqYWNlbmN5KGcpLCBucm93PXZjb3VudChnKSkKIyBHZXQgc3VtIG9mIG5laWdoYm9ycycgZGVncmVlLiAlKiUgaXMgbWF0cml4IG11bHRpcGxpY2F0aW9uLgphMiA8LSBhJSolbWF0cml4KGRlZ3JlZShnKSwgbnJvdz12Y291bnQoZykpCiMgTkNEQyBpcyB0aGUgY29sdW1uIHN1bSBvZiB0aGF0IG1hdHJpeApWKGcpJG5jZGMyIDwtIGEyCgojQ29uZmlybQpWKGcpJG5jZGMgPT0gYTIKClYoZykkbmNkYyA9PSBWKGcpJG5jZGMyCmBgYAoKKipXaGVyZSBkbyBJIGNyZWF0ZSB0aGUgdmVydGV4IGF0dHJpYnV0ZSBmb3IgdGhlIE5DREMgbWVhc3VyZT8qKiBGb3IgZWFjaCBub2RlLCB3ZSBoYXZlIHRvIGdldCBpdHMgbmVpZ2hib3JzIGFuZCBmb3IgZWFjaCBuZWlnaGJvciwgZ2V0IGlzIGRlZ3JlZS4gV2UgdGhlbiBzdW0gdGhlIGRlZ3JlZSBvZiBlYWNoIG5laWdoYm9yIGFuZCBtYWtlIHRoYXQgdGhlIHZlcnRleCBhdHRyaWJ1dGUgc28gd2UgY2FuIGVhc2lseSBjYWxsIG9uIGl0IGZvciB0aGUgTkNEQyBtZWFzdXJlLgoKKipXaHkgZG8gSSByZXNldCBgdG90YWwgPC0gMGAgZm9yIGVhY2ggbmV3IGxvb3A/KiogV2UgaGF2ZSB0byBtYWtlIHRoZSBkaXJlY3RlZCBuZXR3b3JrIHVuZGlyZWN0ZWQgYW5kIHRoZW4gbG9vcCBvdmVyIGV2ZXJ5IG5vZGUuCgpOb3csIGxldCB1cyBtYWtlIHBsb3RzLiAKYGBge3J9CmwgPC0gbGF5b3V0LmZydWNodGVybWFuLnJlaW5nb2xkKGcpCgojIEJlbG93IGdyYXBoIHdpbGwgYmUgdWdseS4KcGxvdChnLCB2ZXJ0ZXguc2l6ZT1WKGcpJG5jZGMsIG1haW49IlNpemUgYnkgTkNEQyIsIGxheW91dD1sKQoKIyBCZWxvdyBncmFwaCB3aWxsIGJlIGJldHRlci4KcGxvdChnLCB2ZXJ0ZXguc2l6ZT1WKGcpJG5jZGMvNTAsIG1haW49IlNpemUgYnkgTkNEQy81MCIsIGxheW91dD1sKQoKIyBQbG90IE5DREMgYWdhaW5zdCBEZWdyZWUKIyBOb3RpY2UgaG93IHRvIGxhYmVsIHBvaW50cwpwbG90KGRlZ3JlZUNlbnRyYWxpdHksIFYoZykkbmNkYywgeGxhYj0nRGVncmVlJywgeWxhYj0nTkNEQycsIHBjaD0nJykKdGV4dCh4PWRlZ3JlZUNlbnRyYWxpdHksIHk9VihnKSRuY2RjLCBsYWJlbHM9VihnKSkKCiMgUGxvdCByYW5rIG9yZGVyaW5nLgpyZEMgPC0gcmFuayhkZWdyZWVDZW50cmFsaXR5KQpyTkNEQyA8LSByYW5rKFYoZykkbmNkYykKCnBsb3QocmRDLCByTkNEQywgeGxhYj0nRGVncmVlIFJhbmsnLCB5bGFiPSdOQ0RDIFJhbmsnLCBwY2g9JycpCnRleHQoeD1yZEMsIHk9ck5DREMsIGxhYmVscz1WKGcpKQpgYGAKCkxvYWQgdGhlIGthcmF0ZSBjbHViIGRhdGE6CmBgYHtyfQpkYXRhKGthcmF0ZSkKayA8LSBhcy51bmRpcmVjdGVkKGthcmF0ZSwgbW9kZT0nY29sbGFwc2UnKQogIAojIENhbGN1bGF0ZSBOQ0RDIGZvciBlYWNoIG5vZGUuCmEgPC0gbWF0cml4KGdldC5hZGphY2VuY3koayksIG5yb3c9dmNvdW50KGspKQpkZWdyZWVzIDwtIG1hdHJpeChkZWdyZWUoayksIG5yb3c9dmNvdW50KGspKQoKYTIgPC0gYSUqJWRlZ3JlZXMKClYoaykkbmNkYyA8LSBhMgoKIyBTYW1lIGFzIGFib3ZlLCB3aXRoIGEgbG9vcApWKGthcmF0ZSkkbmNkYzIgPC0gTkEKZm9yKGkgaW4gMTp2Y291bnQoaykpewogIHRvdGFsIDwtIDAKICBuIDwtIG5laWdoYm9ycyhrLCBWKGspW2ldKQogIGZvcihqIGluIDE6bGVuZ3RoKG4pKXsKICAgIGQgPC0gZGVncmVlKGssIHY9bltqXSkKICAgIHRvdGFsIDwtIHRvdGFsK2QKICB9CiAgVihrKSRuY2RjMltpXSA8LSB0b3RhbAp9CgojIENvbmZpcm0KVihrKSRuY2RjMiA9PSBWKGspJG5jZGMKCiMgTWFrZSBhIG5ldHdvcmsgcGxvdCB1c2luZyBhIHJpbmcgbGF5b3V0IHdoZXJlIGVhY2ggbm9kZSBpcyBzaXplZCBieSBOQ0RDLgpyIDwtIGxheW91dF9pbl9jaXJjbGUoaykKcGxvdChrLCBsYXlvdXQ9ciwgdmVydGV4LnNpemU9VihrKSRuY2RjLzUpCgojIFBsb3QgdGhlIGNvcnJlbGF0aW9uIG9mIE5DREMgYW5kIGRlZ3JlZSBjZW50cmFsaXR5LgpwbG90KGRlZ3JlZShrKSwgVihrKSRuY2RjLCBwY2g9JycsIHhsYWI9J0RlZ3JlZScsIHlsYWI9J05DREMnKQp0ZXh0KGRlZ3JlZShrKSwgVihrKSRuY2RjLCBsYWJlbD1WKGspJG5hbWUpCmBgYAoKCiMgQWxsIFN0ZXBzIENlbnRyYWxpdHkKSW4gdGhpcyBzZWN0aW9uLCB3ZSB3aWxsIGV4YW1pbmUgdGhlIGJldHdlZW5uZXNzIGFuZCBlaWdlbnZlY3RvciBjZW50cmFsaXR5IGZvciB0aGUga2FyYXRlIGNsdWIgbmV0d29yay4gIFNlZSBwYWdlIDk1IG9mIEx1a2UgMjAxNSBmb3IgYSBsaXN0IG9mIHRoZSBgaWdyYXBoYCBjZW50cmFsaXR5IGNvbW1hbmRzLgpgYGB7cn0KZGF0YShrYXJhdGUpCgojIEdldCBiZXR3ZWVubmVzcyBjZW50cmFsaXR5ClYoa2FyYXRlKSRiYyA8LSBiZXR3ZWVubmVzcyhrYXJhdGUsIGRpcmVjdGVkPUZBTFNFKQoKIyBHZXQgYmV0d2Vlbm5lc3MgY2VudHJhbGl0eSwgbm9ybWFsaXplZApWKGthcmF0ZSkkYmNfbm9ybSA8LSBiZXR3ZWVubmVzcyhrYXJhdGUsIGRpcmVjdGVkPUZBTFNFLCBub3JtYWxpemVkPVRSVUUpCgojIEdldCBlaWdlbnZlY3RvciBjZW50cmFsaXR5ClYoa2FyYXRlKSRlYyA8LSBldmNlbnQoa2FyYXRlKQoKIyBIbSwgaXQgZmFpbHMuICBXaHk/CmV2Y2VudChrYXJhdGUpCgojIE9rLCBpdCByZXR1cm5zIGEgbGlzdCwgYW5kIG9uZSBvZiB0aGUgbGlzdCBlbnRyaWVzIGlzIHRoZSBlaWdlbnZlY3RvciBjZW50cmFsaXR5LgpWKGthcmF0ZSkkZWMgPC0gZXZjZW50KGthcmF0ZSkkdmVjdG9yCgojIENvcnJlbGF0aW9uCmNvcihWKGthcmF0ZSkkYmMsIFYoa2FyYXRlKSRlYykKCiMjIFdoeSBkb2VzIHRoYXQgZmFpbD8gTGV0J3MgaW5zcGVjdC4KIyBMb29rcyBnb29kClYoa2FyYXRlKSRiYwojIFN0aWxsIHNhdmVkIGFzIGEgbGlzdC4KVihrYXJhdGUpJGVjCgojIEZpeC4gIEhhdmUgdG8gcmVtb3ZlIHRoZSBhdHRyaWJ1dGUgc28gdGhhdCBSIGRvZXMgbm90IHByZXNlcnZlIHRoZSBsaXN0IHN0cnVjdHVyZS4gIFRoYXQgaXMsIGVhcmxpZXIgSSBoYWQgbm90IHJlbW92ZWQgdGhlIGxpc3QsIHNvIFIganVzdCBhZGRlZCBhbiBlbnRyeSB0byB0aGF0IGxpc3QuCmthcmF0ZSA8LSByZW1vdmUudmVydGV4LmF0dHJpYnV0ZShrYXJhdGUsICdlYycpCgojdW5saXN0KCkgd2lsbCBmbGF0dGVuIHRoZSBsaXN0IHlvdSBnaXZlIGl0LgpWKGthcmF0ZSkkZWMgPC0gdW5saXN0KGV2Y2VudChrYXJhdGUpJHZlY3RvcikKCiMgQ29ycmVsYXRpb24KY29yKFYoa2FyYXRlKSRiYywgVihrYXJhdGUpJGVjKQoKIyBDb3JyZWxhdGlvbiBvZiBtdWx0aXBsZSB2YXJpYWJsZXMKY29yKGRhdGEuZnJhbWUoVihrYXJhdGUpJGJjLCBWKGthcmF0ZSkkZWMsIGRlZ3JlZShrYXJhdGUpKSkKYGBgCgpOb3csIHdlIHdpbGwgbWFrZSBwbG90czoKYGBge3J9CmwgPC0gbGF5b3V0LmZydWNodGVybWFuLnJlaW5nb2xkKGthcmF0ZSkKCiMgQmV0d2Vlbm5lc3MuICBOb2RlcyB3aWxsIGJlIHRvbyBiaWcKcGxvdChrYXJhdGUsIHZlcnRleC5zaXplPVYoa2FyYXRlKSRiYywgbWFpbj0iU2l6ZSBieSBCZXR3ZWVubmVzcyIsIGxheW91dD1sKQoKIyBCZXR3ZWVubmVzcy4gIERvd25zaXplIG5vZGVzCnBsb3Qoa2FyYXRlLCB2ZXJ0ZXguc2l6ZT1WKGthcmF0ZSkkYmMvMTAsIG1haW49IlNpemUgYnkgQmV0d2Vlbm5lc3MsIFJlc2NhbGUiLCBsYXlvdXQ9bCkKCiMgQmV0d2Vlbm5lc3MsIG5vcm1hbGl6ZWQuCiMgQWRkIG9uZSBzbyB0aGF0IHRoZXkgYXJlIG5vdCB0b28gc21hbGwuCnBsb3Qoa2FyYXRlLCB2ZXJ0ZXguc2l6ZT1WKGthcmF0ZSkkYmNfbm9ybSArIDEsIG1haW49IlNpemUgYnkgTm9ybWFsaXplZCBCZXR3ZWVubmVzcyIsIGxheW91dD1sKQoKIyBFaWdlbnZlY3RvcgpwbG90KGthcmF0ZSwgdmVydGV4LnNpemU9VihrYXJhdGUpJGVjKjEwLCBtYWluPSJTaXplIGJ5IEVpZ2VudmVjdG9yKjEwIiwgbGF5b3V0PWwpCgojIFBsb3QgQmV0d2Vlbm5lc3MgVmVyc3VzIEVpZ2VudmVjdG9yCiMgSSBjaGFuZ2UgdGhlIHhsaW1pdCBzbyB0aGF0IHRoZSB2ZXJ0ZXggbGFiZWxzIGFyZSBub3QgY3V0IG9mZi4KcGxvdChWKGthcmF0ZSkkYmMsIFYoa2FyYXRlKSRlYywgeGxhYj0nQmV0d2Vlbm5lc3MgQ2VudHJhbGl0eScsIHlsYWI9J0VpZ2VudmVjdG9yIENlbnRyYWxpdHknLCBwY2g9JycsIHhsaW09YygtMjAsIG1heChWKGthcmF0ZSkkYmMpKjEuMSkpCnRleHQoeD1WKGthcmF0ZSkkYmMsIHk9VihrYXJhdGUpJGVjLCBsYWJlbHM9VihrYXJhdGUpJG5hbWUpCgojIFdoYXQgaGFwcGVucyBpZiBJIGRvIG5vdCBjaGFuZ2UgdGhlIHggbGltaXQ/ICBXZSBzZWUgdGhlIGxhYmVscyBnZXQgY3V0IG9mZi4KcGxvdChWKGthcmF0ZSkkYmMsIFYoa2FyYXRlKSRlYywgeGxhYj0nQmV0d2Vlbm5lc3MgQ2VudHJhbGl0eScsIHlsYWI9J0VpZ2VudmVjdG9yIENlbnRyYWxpdHknLCBwY2g9JycpCnRleHQoeD1WKGthcmF0ZSkkYmMsIHk9VihrYXJhdGUpJGVjLCBsYWJlbHM9VihrYXJhdGUpJG5hbWUpCgojIFdlIGNhbiBjb2xvciB0aGUgdGV4dCBsYWJlbHMKY29sb3JzIDwtIGlmZWxzZShWKGthcmF0ZSkkY29sb3I9PTEsICdyZWQnLCAnYmxhY2snKQoKIyBTYW1lIHBsb3QgYXMgYWJvdmUsIGJ1dCBsZXQgdXMgYWxzbyBjb2xvciBieSBncm91cApwbG90KFYoa2FyYXRlKSRiYywgVihrYXJhdGUpJGVjLCB4bGFiPSdCZXR3ZWVubmVzcyBDZW50cmFsaXR5JywgeWxhYj0nRWlnZW52ZWN0b3IgQ2VudHJhbGl0eScsIHBjaD0nJywgeGxpbT1jKC0yMCwgbWF4KFYoa2FyYXRlKSRiYykqMS4xKSkKdGV4dCh4PVYoa2FyYXRlKSRiYywgeT1WKGthcmF0ZSkkZWMsIGxhYmVscz1WKGthcmF0ZSkkbmFtZSwgY29sPWNvbG9ycykKCmBgYAoKCioqV2hpY2ggbm9kZXMgaGF2ZSBoaWdoIGJldHdlZW5uZXNzIGFuZCBlaWdlbnZlY3RvciBjZW50cmFsaXR5PyoqCgoqKldoaWNoIG5vZGVzIGhhdmUgbG93IGJldHdlZW5uZXNzIGJ1dCBoaWdoIGVpZ2VudmVjdG9yIGNlbnRyYWxpdHk/KioKCioqV2hpY2ggbm9kZXMgaGF2ZSBoaWdoIGJldHdlZW5lc3MgYnV0IGxvdyBlaWdlbnZlY3RvciB2ZW50cmFsaXR5PyoqCgoKVGhlIGZvbGxvd2luZyBxdWVzdGlvbnMgdXNlIHRoZSBgVUtmYWN1bHR5YCBkYXRhLiAgUHJldGVuZCBsaWtlIHlvdSBoYXZlIG5vdCBsb2FkZWQgaXQgYWxyZWFkeS4gIE5vdGUgdGhhdCB5b3UgY291bGQgbWFrZSB0aGUgZ3JhcGggdW5kaXJlY3RlZCBvciB5b3UgY2FuIHRlbGwgZWFjaCBmdW5jdGlvbiB0byBhc3N1bWUgdGhlIGdyYXBoIGlzIG5vdCBkaXJlY3RlZDoKYGBge3J9CiMjIExvYWQgdGhlIFVLZmFjdWx0eSBkYXRhCmRhdGEoVUtmYWN1bHR5KQpnIDwtIGFzLnVuZGlyZWN0ZWQoVUtmYWN1bHR5LCBtb2RlPWMoJ2NvbGxhcHNlJykpCgojIyBDYWxjdWxhdGUgdGhlIGJldHdlZW5uZXNzIGNlbnRyYWxpdHkgb2YgZWFjaCBub2RlLgpWKGcpJGJjIDwtIGJldHdlZW5uZXNzKGcsIGRpcmVjdGVkPUZBTFNFKQpWKGcpJGJjX25vcm0gPC0gYmV0d2Vlbm5lc3MoZywgZGlyZWN0ZWQ9RkFMU0UsIG5vcm1hbGl6ZWQ9VFJVRSkKCiMjIENhbGN1bGF0ZSB0aGUgZWlnZW52ZWN0b3IgY2VudHJhbGl0eSBvZiBlYWNoIG5vZGUuClYoZykkZWMgPC0gZXZjZW50KGcpJHZlY3RvcgoKIyMgRHJhdyBhIG5ldHdvcmsgZ3JhcGgsIHNpemluZyBlYWNoIG5vZGUgYnkgYmV0d2Vlbm5lc3MgY2VudHJhbGl0eS4KZiA8LSBsYXlvdXQuZnJ1Y2h0ZXJtYW4ucmVpbmdvbGQoZykKcGxvdChnLCBsYXlvdXQ9ZiwgdmVydGV4LnNpemU9VihnKSRiYy8yNSkKCiMjIERyYXcgYSBuZXR3b3JrIGdyYXBoLCBzaXppbmcgZWFjaCBub2RlIGJ5IGVpZ2VudmVjdG9yIGNlbnRyYWxpdHkuCnBsb3QoZywgbGF5b3V0PWYsIHZlcnRleC5zaXplPVYoZykkZWMqNTApCgojIyBNYWtlIGEgc2NhdHRlcnBsb3Qgb2YgZWlnZW52ZWN0b3IgYW5kIGJldHdlZW5uZXNzIGNlbnRyYWxpdHkuICBMYWJlbCB0aGUgYXhlcy4gIERvIG5vdCBsYWJlbCB0aGUgcG9pbnRzLgpwbG90KFYoZykkYmMsIFYoZykkZWMsIHhsYWI9J0JldHdlZW5uZXNzIENlbnRyYWxpeScsIHlsYWI9J0VpZ2VudmVjdG9yIENlbnRyYWxpdHknKQoKIyMgUmVtYWtlIHRoZSBwcmV2aW91cyBzY2F0dGVycGxvdCwgbGFiZWxpbmcgdGhlIHBvaW50cy4gIFNwbGl0IHRoZW0gaW50byAyIGdyb3VwcyBhbmQgY29sb3IgdGhlbSBieSBncm91cC4KY29sb3JzIDwtIGlmZWxzZShWKGcpJEdyb3VwIDw9IDIsICdibHVlJywgJ29yYW5nZScpCnBsb3QoVihnKSRiYywgVihnKSRlYywgeGxhYj0nQmV0d2Vlbm5lc3MgQ2VudHJhbGl5JywgeWxhYj0nRWlnZW52ZWN0b3IgQ2VudHJhbGl0eScsIHBjaD0nJykKdGV4dChWKGcpJGJjLCBWKGcpJGVjLCBsYWJlbD1WKGcpJEdyb3VwLCBjb2w9Y29sb3JzKQpgYGAKCgoKCgoKCgoKCgoKCgoKIyMjIyBMT09QUwoKV3JpdGUgYSBsb29wIHRoYXQgcHJpbnRzIHRoZSBwaHJhc2UgIkhlbGxvIFdvcmxkIiB0ZW4gdGltZXMuCmBgYHtyfQpmb3IgKGluZGV4IGluIDE6MTApIHsgCiAgcHJpbnQoIkhlbGxvIFdvcmxkIikKfQpgYGAKCldyaXRlIGEgbG9vcCB0aGF0IHByaW50cyB0aGUgbnVtYmVyIG9mIHRoZSBsb29wIG11bHRpcGxpZWQgYnkgMTAuIEZvciBleGFtcGxlLCB0aGUgZmlyc3QgaXRlcmF0aW9uIHByaW50cyBgMTBgLCB0aGUgc2Vjb25kIGAyMGAsIGFuZCBzbyBvbi4gIERvIHRoaXMgcHJvY2VkdXJlIGZvciAyMCBpdGVyYXRpb25zLgpgYGB7cn0KZm9yICh0aGlzaXRlcmF0aW9uIGluIDE6MjApIHsKcHJpbnQodGhpc2l0ZXJhdGlvbiAqIDEwKSAKICB9CmBgYAoKYGBge3J9CmZvcihpIGluIDE6MTApeyAgIyBGb3IgZWFjaCBvZiB0ZW4gaXRlcmF0aW9ucwogIHByaW50KCdIZWxsbyB3b3JsZCcpICAjIHByaW50IHRoZSBwaHJhc2UsICJIZWxsbyB3b3JsZCIKICBwcmludCgyKmkpCn0gCgpmb3IoaSBpbiAxOjEyKXsKICBwcmludCgnaGVsbG8gd29ybGQnKQogIHByaW50KDIqaSkKfQpgYGAKCkhvdyB3b3VsZCB5b3UgY2hhbmdlIHRoZSBhYm92ZSBsb29wIHRvIHByaW50IGEgc3RhdGVtZW50IDIwIHRpbWVzPwpgYGB7cn0KZm9yKGkgaW4gMToyMCl7ICAjIEZvciBlYWNoIG9mIHR3ZW50eSBpdGVyYXRpb25zCiAgcHJpbnQoJ0hlbGxvIHdvcmxkJykgICMgcHJpbnQgdGhlIHBocmFzZSwgIkhlbGxvIHdvcmxkIgogIHByaW50KDIqaSkKfSAKYGBgCgpWZWN0b3JzIGFyZSBmYXN0ZXIgbG9vcHMuIElmIHlvdSBhcmUgZ29pbmcgdG8gY3JlYXRlIGEgdmVjdG9yIGR1cmluZyBhIGxvb3AsIGl0IG5lZWRzIHRvIGJlIGRlZmluZWQgYmVmb3JlIHRoZSBsb29wLgpgYGB7cn0Kb25lIDwtIGMoMTAsMjAsMjAsMTgsIDIwLCAzMCkKdHdvIDwtIGMoMSw1LDMsOSwgNTAsIDEwKQpyZXN1bHQgPC0gTlVMTCAKCmZvcihpIGluIDE6bGVuZ3RoKHR3bykpeyAgIyBOb3RpY2UgaG93IEkgZHluYW1pY2FsbHkgY2hhbmdlZAogIHByaW50KHJlc3VsdFtpXSA8LSBvbmVbaV0qdHdvW2ldKQp9Cgphc3ZlY3RvcnMgPC0gb25lKnR3bwoKcmVzdWx0ID09IGFzdmVjdG9ycwpgYGAKCmkgaXMgdXNlZCB0byBpbmRleCBwbGFjZSBieSBjb252ZW50aW9uLCBidXQgeW91IGNhbiB1c2UgYW55dGhpbmc6CmBgYHtyfQpmb3IoeiBpbiAxOjUpewogIHByaW50KHopCn0KCmZvcih3YXl0b29sb25naW5kZXggaW4gMTo1KXsKICBwcmludCh3YXl0b29sb25naW5kZXgpCn0KYGBgCgpZb3UgY291bGQgZXZlbiB1c2UgaXQgdG8gbWFrZSBhIGxvdCBvZiBncmFwaHMuIERvIG5vdCBmb3JnZXQgdG8gY2hhbmdlIHRoZSBmaWxlcGF0aCB0byBiZSB5b3VyIGNvbXB1dGVyLgpgYGB7cn0KZCA8LSByZWFkLmNzdigiaHR0cHM6Ly9zdGF0cy5pZHJlLnVjbGEuZWR1L3N0YXQvZGF0YS9oc2JyYXcuY3N2Iiwgc3RyaW5nc0FzRmFjdG9ycyA9IEZBTFNFKQoKeWNvbHVtbnMgPC0gYygnd3JpdGUnLCAnbWF0aCcsICdzY2llbmNlJywgJ3NvY3N0JykKCgpmb3IoaSBpbiAxOmxlbmd0aCh5Y29sdW1ucykpewogIGZpbGVuYW1lIDwtIHBhc3RlMCgnfi9EZXNrdG9wL2xvb3BwbG90ZXhhbXBsZV8nLCB5Y29sdW1uc1tpXSwnLnBkZicpCiAgcGRmKGZpbGVuYW1lKQogIHBsb3QoZCRyZWFkLCBkW1t5Y29sdW1uc1tpXV1dLCBwY2g9MjAsIHhsYWI9J1JlYWRpbmcgU2NvcmUnLCB5bGFiPXljb2x1bW5zW2ldKQogIGRldi5vZmYoKQp9CgpxKCkKYGBgCgpJdCBpcyBoYXJkIHRvIGtlZXAgZWFjaCB2YWx1ZSBzdHJhaWdodCwgaG93ZXZlci4gIEFuZCBpZiB3ZSB3YW50IHRvIHJldHVybiB0byB0aGVzZSByZXN1bHRzIGxhdGVyLCB3ZSBuZWVkIHRvIHJlcnVuIHRoZSBsb29wLiAgRG9pbmcgc28gaXMgYW5ub3lpbmcgYW5kIHdpbGwgY3JlYXRlIHNsaWdodGx5IGRpZmZlcmVudCBhbnN3ZXJzIGJlY2F1c2Ugb2YgdGhlIHJhbmRvbSBudW1iZXIgZ2VuZXJhdG9yLiAgTGV0IHVzIGxlYXJuIGhvdyB0byBzYXZlIHRoZSBkYXRhIGFzIHRoZXkgYXJlIGNyZWF0ZWQuCgpJbiB0aGUgbG9vcCwgSSBzaG93IHR3byB3YXlzIHRvIGNyZWF0ZSB0aGUgZGF0YS4gIFRoZSBjb21tZW50ZWQgb25lLCB3aXRoIGAjYCBpbiBmcm9udCBvZiBpdCwgZG9lcyBub3QgcnVuIGJ1dCBtYXkgYmUgZWFzaWVyIHRvIHJlYWQuICBUbyB1c2UgaXQsIHJlbW92ZSB0aGUgYCNgIGZyb20gZWFjaCBsaW5lIGFuZCBwdXQgYCNgIGluIGZyb250IG9mIGxpbmVzIHRoYXQgYXJlIGN1cnJlbnRseSBleGVjdXRlZC4gIFRvIGFkZCBvciByZW1vdmUgY29tbWVudHMgdG8gbXVsdGlwbGUgbGluZXMsIGhpZ2hsaWdodCB0aGUgb25lcyB5b3Ugd2FudCB0byBjaGFuZ2UgYW5kIHR5cGUgYGNtZGArYGNgOyBvbiBhIFdpbmRvd3MgbWFjaGluZSwgeW91IHByb2JhYmx5IHdpbGwgcmVwbGFjZSBgY21kYCB3aXRoIHRoZSBmbGFnIGtleS4KYGBge3J9CnJhbmRvbW5lc3MgPC0gYyguMDEsIC4xLCAuMywgLjUsIC43LCAuOSwgMSkKaXRlcmF0aW9ucyA8LSBsZW5ndGgocmFuZG9tbmVzcykgICMgV2h5IGRpZCBJIGRlZmluZSBpdGVyYXRpb25zIHRoaXMgd2F5Pwpob3dtYW55cm93cyA8LSAxMDAKCnJlbWVtYmVyIDwtIGRhdGEuZnJhbWUoaXRlcmF0aW9uPU5VTEwsIHRpZVByb2JhYmlsaXR5PU5VTEwsIGF2Z0RlZ3JlZT1OVUxMLCBnbG9iYWxDbHVzdGVyaW5nPU5VTEwsIGdsYmFsQ2x1c3RlcmluZzI9TlVMTCwgZGVuc2l0eT1OVUxMKQoKZm9yKGkgaW4gMTppdGVyYXRpb25zKXsKICB0ZW1wIDwtIGVyZG9zLnJlbnlpLmdhbWUoaG93bWFueXJvd3MsIHA9cmFuZG9tbmVzc1tpXSkKICAKICBpdGVyYXRpb24gPC0gaQogIHRpZVByb2JhYmlsaXR5IDwtIHJhbmRvbW5lc3NbaV0KICBhdmdEZWdyZWUgPC0gbWVhbihkZWdyZWUodGVtcCkpCiAgZ2xvYmFsQ2x1c3RlcmluZyA8LSB0cmFuc2l0aXZpdHkodGVtcCwgdHlwZT0nZ2xvYmFsJykKICBnbG9iYWxDbHVzdGVyaW5nMiA8LSBtZWFuKHRyYW5zaXRpdml0eSh0ZW1wLCB0eXBlPSdsb2NhbCcpKQogIGRlbnNpdHkgPC0gZ3JhcGguZGVuc2l0eSh0ZW1wKQogICMgCiAgIyBkIDwtIGRhdGEuZnJhbWUoaXRlcmF0aW9uPWksIHRpZVByb2JhYmlsaXR5PXJhbmRvbW5lc3NbaV0sIGF2Z0RlZ3JlZT1tZWFuKGRlZ3JlZSh0ZW1wKSksIGdsb2JhbENsdXN0ZXJpbmc9dHJhbnNpdGl2aXR5KHRlbXAsIHR5cGU9J2dsb2JhbCcpLCBnbG9iYWxDbHVzdGVyaW5nMj1tZWFuKHRyYW5zaXRpdml0eSh0ZW1wLCB0eXBlPSdsb2NhbCcpKSwgZGVuc2l0eT1ncmFwaC5kZW5zaXR5KHRlbXApKQogIAogIGQgPC0gZGF0YS5mcmFtZShpdGVyYXRpb249aXRlcmF0aW9uLCB0aWVQcm9iYWJpbGl0eT10aWVQcm9iYWJpbGl0eSwgYXZnRGVncmVlPWF2Z0RlZ3JlZSwgZ2xvYmFsQ2x1c3RlcmluZz1nbG9iYWxDbHVzdGVyaW5nLCBnbG9iYWxDbHVzdGVyaW5nMj1nbG9iYWxDbHVzdGVyaW5nMiwgZGVuc2l0eT1kZW5zaXR5KQogIHJlbWVtYmVyIDwtIHJiaW5kKHJlbWVtYmVyLCBkKSAgIyAicm93YmluZCIsIGFkZHMgYSBuZXcgcm93LCBkLCB0byB3aGF0ZXZlciBleGlzdHMgaW4gcmVtZW1iZXIuCiAgI3ByaW50KHJlbWVtYmVyKQp9CgpyZW1lbWJlciAjIENvb2wuCgpgYGAKClVzZSBhIGxvb3AgYW5kIGlncmFwaCB0byBjcmVhdGUgcmFuZG9tIG5ldHdvcmtzLiAgVmFyeSB0aGUgcHJvYmFiaWxpdHkgb2YgZm9ybWluZyBhbiBlZGdlIDEwIHRpbWVzLiAgUHV0IHNvbWUgcmVzdWx0aW5nIHN0YXRpc3RpY3MgaW4gYSBkYXRhZnJhbWUuIApgYGB7cn0KcmVtZW1iZXIyIDwtIGRhdGEuZnJhbWUoaXRlcmF0aW9uPU5VTEwsIHRpZVByb2JhYmlsaXR5PU5VTEwsIGF2Z0RlZ3JlZT1OVUxMLCBnbG9iYWxDbHVzdGVyaW5nPU5VTEwsIGdsYmFsQ2x1c3RlcmluZzI9TlVMTCwgZGVuc2l0eT1OVUxMKQoKdGhlcHMgPC0gYyguMDEsIC4xLCAuMiwgLjMsIC40LCAuNSwgLjYsIC43LCAuOCwgLjkpCml0ZXJhdGlvbnMgPC0gbGVuZ3RoKHRoZXBzKQoKZm9yKGkgaW4gMTppdGVyYXRpb25zKXsKICB0ZW1wIDwtIGVyZG9zLnJlbnlpLmdhbWUoaG93bWFueXJvd3MsIHA9cmFuZG9tbmVzc1tpXSkKICAKICAjIGl0ZXJhdGlvbiA8LSBpCiAgIyB0aWVQcm9iYWJpbGl0eSA8LSByYW5kb21uZXNzW2ldCiAgIyBhdmdEZWdyZWUgPC0gbWVhbihkZWdyZWUodGVtcCkpCiAgIyBnbG9iYWxDbHVzdGVyaW5nIDwtIHRyYW5zaXRpdml0eSh0ZW1wLCB0eXBlPSdnbG9iYWwnKQogICMgZ2xvYmFsQ2x1c3RlcmluZzIgPC0gbWVhbih0cmFuc2l0aXZpdHkodGVtcCwgdHlwZT0nbG9jYWwnKSkKICAjIGRlbnNpdHkgPC0gZ3JhcGguZGVuc2l0eSh0ZW1wKQogICMgCiAgZCA8LSBkYXRhLmZyYW1lKGl0ZXJhdGlvbj1pLCB0aWVQcm9iYWJpbGl0eT1yYW5kb21uZXNzW2ldLCBhdmdEZWdyZWU9bWVhbihkZWdyZWUodGVtcCkpLCBnbG9iYWxDbHVzdGVyaW5nPXRyYW5zaXRpdml0eSh0ZW1wLCB0eXBlPSdnbG9iYWwnKSwgZ2xvYmFsQ2x1c3RlcmluZzI9bWVhbih0cmFuc2l0aXZpdHkodGVtcCwgdHlwZT0nbG9jYWwnKSksIGRlbnNpdHk9Z3JhcGguZGVuc2l0eSh0ZW1wKSkKICAKICAjIGQgPC0gZGF0YS5mcmFtZShpdGVyYXRpb249aXRlcmF0aW9uLCB0aWVQcm9iYWJpbGl0eT10aWVQcm9iYWJpbGl0eSwgYXZnRGVncmVlPWF2Z0RlZ3JlZSwgZ2xvYmFsQ2x1c3RlcmluZz1nbG9iYWxDbHVzdGVyaW5nLCBnbG9iYWxDbHVzdGVyaW5nMj1nbG9iYWxDbHVzdGVyaW5nMiwgZGVuc2l0eT1kZW5zaXR5KQogIHJlbWVtYmVyMiA8LSByYmluZChyZW1lbWJlcjIsIGQpICAjICJyb3diaW5kIiwgYWRkcyBhIG5ldyByb3csIGQsIHRvIHdoYXRldmVyIGV4aXN0cyBpbiByZW1lbWJlci4KICAjcHJpbnQocmVtZW1iZXIpCn0KCnJlbWVtYmVyMgoKbnJvdyhyZW1lbWJlcjIpCm5yb3cocmVtZW1iZXIpCmBgYAoKVmFyeSBwYXJhbWV0ZXJzIHVzaW5nIGEgbG9vcDoKYGBge3J9CnJhbmRvbW5lc3MgPC0gYyguMDEsIC4xLCAuMywgLjUsIC43LCAuOSwgMSkKaXRlcmF0aW9ucyA8LSBsZW5ndGgocmFuZG9tbmVzcykgICMgV2h5IGRpZCBJIGRlZmluZSBpdGVyYXRpb25zIHRoaXMgd2F5Pwpob3dtYW55cm93cyA8LSAxMDAKCiMgV2lsbCBub3Qgc2hvdyBhbnl0aGluZwpmb3IoaSBpbiAxOml0ZXJhdGlvbnMpewogIHRlbXAgPC0gZXJkb3MucmVueWkuZ2FtZShob3dtYW55cm93cywgcD1yYW5kb21uZXNzW2ldKQogIHN1bW1hcnkoZGVncmVlKHRlbXApKQogIHRyYW5zaXRpdml0eSh0ZW1wLCB0eXBlPSdnbG9iYWwnKQogIG1lYW4odHJhbnNpdGl2aXR5KHRlbXAsIHR5cGU9J2xvY2FsJykpCiAgZ3JhcGguZGVuc2l0eSh0ZW1wKQp9CgojIE5lZWQgdG8gcHJpbnQKZm9yKGkgaW4gMTppdGVyYXRpb25zKXsKICB0ZW1wIDwtIGVyZG9zLnJlbnlpLmdhbWUoaG93bWFueXJvd3MsIHA9cmFuZG9tbmVzc1tpXSkKICBwcmludChwYXN0ZTAoJ09uIHRyaWFsICcsIGkpKSAgIyBUaGlzIGxpbmUgbGV0cyBtZSBrbm93IHdoaWNoIGl0ZXJhdGlvbiBJIGFtIG9uCiAgcHJpbnQoJ1N1bW1hcnkgU3RhdGlzdGljczonKQogIHByaW50KHN1bW1hcnkoZGVncmVlKHRlbXApKSkKICBwcmludChwYXN0ZTAoJ0dsb2JhbCBjbHVzdGVyaW5nIGlzOiAnLCB0cmFuc2l0aXZpdHkodGVtcCwgdHlwZT0nZ2xvYmFsJykpKQogIHByaW50KCdBdmVyYWdlIG9mIHRoZSBlYWNoIG5vZGVcJ3MgY2x1c3RlcmluZyBpczogJykgICMgTWF5YmUgeW91IGZpbmQgaXQgZWFzaWVyIHRvIHJlYWQgdGhlIG91dHB1dCB0aGlzIHdheQogIHByaW50KG1lYW4odHJhbnNpdGl2aXR5KHRlbXAsIHR5cGU9J2xvY2FsJyksIG5hLnJtPVRSVUUpKQogIHByaW50KHBhc3RlMCgnVGhlIGdyYXBoXCdzIGRlbnNpdHkgaXM6ICcsIGdyYXBoLmRlbnNpdHkodGVtcCkpKQp9CmBgYAoKCgoKCgoKCgoKCgoKIyMjIyBLRVlXT1JEUwoKCjEuICoqU2NyaXB0KiogLSBUaGUgdGV4dCBkb2N1bWVudCB0aGF0IGNvbnRhaW5zIHRoZSBjb21tYW5kcy4gIEl0IGNhbiBiZSBpbiBhbnkgZm9ybWF0LCB0aG91Z2ggdXN1YWxseSB5b3Ugd2lsbCBzZWUgLnR4dCBvciAuUi4KCjIuICoqQ29uc29sZSoqIC0gV2hlcmUgeW91IHNlZSB0aGUgY29kZSBydW4gd2hlbiBpdCBpcyBleGVjdXRlZC4gIEluIFIgU3R1ZGlvLCB0aGUgY29uc29sZSBpcyB0aGUgdG9wIHJpZ2h0IHdpbmRvdyBieSBkZWZhdWx0LgoKMy4gKipWZWN0b3IqKiAtIFRoZSBiYXNpYyBidWlsZGluZyBibG9jayBvZiBSLCBhIHZlY3RvciBpcyBhIHNlcXVlbmNlIG9mIGl0ZW1zIHRoYXQgeW91IHdpbGwgbWFuaXB1bGF0ZS4gIEEgdmVjdG9yIGNhbiBjb250YWluIGFzIGZldyBhcyBvbmUgaXRlbQoKNC4gKipPYmplY3QqKiAtIFIgaXMgd2hhdCBpcyBrbm93biBhcyBhbiAib2JqZWN0LW9yaWVudGVkIGxhbmd1YWdlIi4gIFRoaXMgbWVhbnMgdGhlcmUgYXJlIGFic3RyYWN0IGVudGl0aXRlcyAtIHRoZSBvYmplY3RzIC0gdGhhdCBjb250YWluIHRoZSBkYXRhIG9uIHdoaWNoIHlvdSB3b3JrLiAgQW4gZW50aXR5IGNvdWxkIGJlIGEgZGF0YXNldCwgYSBsaXN0IG9mIG51bWJlcnMsIGEgZGF0YWJhc2UsIG9yIGFueXRoaW5nIGVsc2UuCgo1LiAqKkRhdGFmcmFtZSoqIC0gVGhlIHByaW1hcnkgdHlwZSBvZiBvYmplY3QgdXNlZCB0byByZXByZXNlbnQgZGF0YSBpbiBSLiAgVGhpbmsgb2YgYSBkYXRhZnJhbWUgYXMgYSBzcHJlYWRzaGVldDogaXQgaGFzIHJvd3MgYW5kIGNvbHVtbnMsIHdoZXJlIGVhY2ggcm93IHJlcHJlc2VudHMgYSB1bml0IG9mIGFuYWx5c2lzIHdpdGggcmVjb3JkcyBmb3IgZWFjaCBvZiB0aGUgY29sdW1ucyAodGhlIG1lYXN1cmVkIHZhcmlhYmxlcykuICBBIGRhdGFmcmFtZSBpcyBhIGNvbGxlY3Rpb24gb2YgdmVjdG9ycy4gQSBkYXRhZnJhbWUgaXMgYSBtYXRyaXggd2hvc2UgdmVjdG9ycyBjYW4gYmUgZGlmZmVyZW50IGRhdGEgdHlwZXMsIHN1Y2ggYXMgc3RyaW5ncyBhbmQgbnVtYmVycy4gCgo2LiAqKlN0cmluZyoqIC0gSWYgc29tZXRoaW5nIGlzIGEgInN0cmluZyIsIHRoYXQgbWVhbnMgaXQgaXMgYSB3b3JkLiAgQSBzdHJpbmcgaXMgaW4gY29udHJhc3QgdG8gbnVtZXJpYyBkYXRhLgoKNy4gKipQYWNrYWdlKiogLSBBIHNldCBvZiBjb2RlIHRoYXQgZXh0ZW5kcyB0aGUgZnVuY3Rpb25hbGl0eSBvZiBiYXNlIFIuICBFYWNoIHBhY2thZ2Ugd2lsbCBoYXZlIGEgZGlmZmVyZW50IHB1cnBvc2UsIGFuZCB0aGUgY2xhcml0eSBvZiB0aGUgZG9jdW1lbnRhdGlvbiwgYXMgd2VsbCBhcyBmdW5jdGlvbmFsaXR5IG9mIHRoZSBjb2RlLCBpcyBhdCB0aGUgd2hpbSBvZiB0aGUgcGFja2FnZSdzIGNyZWF0b3IuICBBbGwgcGFja2FnZXMgYXJlIGZyZWUgdG8gdXNlLgoKOC4gKipEZWJ1ZyoqLCAqKlJlZmFjdG9yKiogLSBUaGVzZSB0d28gd29yZHMgYXJlIGZhbmN5IHdheXMgb2Ygc2F5aW5nIHlvdSBhcmUgZmluZGluZyBhbmQgY29ycmVjdGluZyBlcnJvcnMgaW4geW91ciBjb2RlLgoKOS4gKipMaXN0KiogLSBBIGxpc3QgaXMgYSBjb2xsZWN0aW9uIG9mIHZlY3RvcnMuICBXaGVyZWFzIGEgdmVjdG9yIG11c3QgY29udGFpbiBkYXRhIG9mIHRoZSBzYW1lIHR5cGUsIGEgbGlzdCBjYW4gY29udGFpbiB2ZWN0b3JzIG9mIGRpZmZlcmVudCB0eXBlcyAoZWFjaCB2ZWN0b3Igc3RpbGwgbXVzdCBvbmx5IGNvbnRhaW4gb25lIGRhdGEgdHlwZSkuCgoxMC4gKipNYXRyaXgqKiAtIEEgbWF0cml4IGlzIGEgc2VyaWVzIG9mIHZlY3RvcnMgb2YgbnVtZXJpYyBkYXRhLiAgQSBtYXRyaXggaGFzIHRvIGNvbnRhaW4gbnVtZXJpYyBkYXRhLiAgVGhleSBhcmUgaW1wb3J0YW50IGJlY2F1c2UgdGhleSBjYW4gcmVwcmVzZW50IHJlbGF0aW9uc2hpcHMgaW4gYSBuZXR3b3JrLCBzbyBjb25zdHJ1Y3RpbmcgYW5kIG1hbmlwdWxhdGluZyB0aGVtIGlzIGltcG9ydGFudCBmb3IgbmV0d29yayBhbmFseXNpcy5TaW5jZSB3ZSBub3cgaGF2ZSBhbiBvYmplY3Qgd2l0aCB0d28gZGltZW5zaW9ucyAocm93cyBhbmQgY29sdW1ucyksIHdlIGFjY2VzcyB0aGVtIGRpZmZlcmVudGx5IHRoYW4gd2UgZGlkIHZlY3RvcnMuIEEgd2hvbGUgcm93IGlzIGBvYmplY3Rbcm93bnVtYmVyLF1gLCBhIHdob2xlIGNvbHVtbiBpcyBgb2JqZWN0Wyxjb2x1bW51bWJlcl1gLCBhbmQgYSBzcGVjaWZpYyBjZWxsIGlzIGBvYmplY3Rbcm93LCBjb2x1bW5dYC4KCjExLiAqKnRpZHl2ZXJzZSoqIC0gaXMgYWN0dWFsbHkgYSBzZXJpZXMgb2YgcGFja2FnZXMgY3JlYXRlZCBieSBIYWRsZXkgV2lja2hhbSwgdGhlIHR3byBtb3N0IGNvbW1vbiBvZiB3aGljaCBhcmUgYGRwbHlyYCBmb3IgZGF0YSBtZXJnaW5nIGFuZCBhZ2dyZWdhdGlvbiBhbmQgYGdncGxvdGAgZm9yIGdlbmVyYXRpbmcgZ3JhcGhpY3MuICBUbyBrZWVwIGZyb20gb3ZlcmxvYWRpbmcgeW91IHdpdGggaW5mb3JtYXRpb24sIEkgd2lsbCBub3QgYWN0dWFsbHkgdXNlIHRoZSBgdGlkeXZlcnNlYCBpbiB0aGlzIGNsYXNzLCBidXQgaXQgaXMgcXVpdGUgY29tbW9uIGluIHRoZSBSIHdvcmxkLiAgYGlncmFwaGAgaXMgdGhlIG5ldHdvcmsgcGFja2FnZSB0aGF0IHdlIHdpbGwgdXNlIHRocm91Z2hvdXQgdGhlIHF1YXJ0ZXIuCgpJbiBhZGRpdGlvbiB0byB1c2luZyBjb2RlLCB5b3UgY2FuIGFsc28gdXNlIGEgZ3JhcGhpY2FsIGludGVyZmFjZSB0byBpbnN0YWxsIHBhY2thZ2VzLiAgSW4gYmFzZSBSLCBjbGljayBgUGFja2FnZXMgJiBEYXRhYCwgdGhlbiBgUGFja2FnZSBJbnN0YWxsZXJgLiAgWW91IGNhbiB0aGVuIHNlYXJjaCBmb3IgdGhlIHBhY2thZ2UgYnkgbmFtZS4gIEluIFIgU3R1ZGlvLCBuYXZpZ2F0ZSB0byBgVG9vbHNgLCB0aGVuIGBJbnN0YWxsIFBhY2thZ2VzYCwgYW5kIHNlYXJjaCBmb3IgdGhlIHBhY2thZ2UgYnkgbmFtZS4gIEJlbG93J3MgY29kZSBzaG93cyBob3cgdG8gZG8gdGhpcyBwcm9jZXNzIGluIHlvdXIgc2NyaXB0LlteMV0KClteMV06IElmIHlvdSBsb29rIGF0IHRoZSBSIE1hcmtkb3duIGZpbGUsIHlvdSB3aWxsIG5vdGljZSB0aGUgb3B0aW9uICJldmFsPUZBTFNFIiwgd2hlcmVhcyB0aGUgb3RoZXIgY29kZSBjaHVua3MgdXNlICJldmFsPVRSVUUiLiAgYGV2YWxgIHRlbGxzIFIgTWFya2Rvd24gd2hldGhlciBvciBub3QgdG8gZXhlY3V0ZSB0aGUgY29kZSBpbiB0aGF0IGNodW5rLiAgU2luY2UgSSBkbyBub3Qgd2FudCB0byBpbnN0YWxsIHRoZXNlIHBhY2thZ2VzIGV2ZXJ5IHRpbWUgSSBjb21waWxlIHRoZSBkb2N1bWVudCwgSSBzZXQgaXQgdG8gYEZBTFNFYCBoZXJlLgoKMTIuICoqQ1NWKiogLSBUaGUgbWFpbiBraW5kIG9mIGRhdGEgeW91IHdpbGwgcmVhZCBpcyBgLmNzdmAsIHdoaWNoIHN0YW5kcyBmb3IgY29tbWEtc2VwYXJhdGVkIHZhbHVlcy4gIEl0IGlzIGEgdGV4dCBmaWxlIHdoZXJlIGVhY2ggY29sdW1uIGlzIHNlcGFyYXRlZCBieSBhIGNvbW1hLiAgTWFrZSBzdXJlIHlvdSBjaGFuZ2UgdGhlIGZpbGUgcGF0aCB0byBiZSB0byB3aGVyZSB5b3VyIGRhdGEgYXJlLiAgTm90ZSB0aGF0IHJlYWQgYHJlYWQuYCBsaW5lIHdvcmtzOyB0aGUgcG9pbnQgaXMgdGhhdCBgcmVhZC5jc3ZgIGlzIGEgc3BlY2lhbCBjYXNlIG9mIGByZWFkLmRlbGltYC4gIFRoaXMgZGlzdGluY3Rpb24gbWF0dGVycyBiZWNhdXNlIHlvdSBjb3VsZCBoYXZlIGRhdGEgdGhhdCBhcmUgc2VwYXJhdGVkIGJ5IGEgZmVhdHVyZSB0aGF0IGlzIG5vdCBhIGNvbW1hLCBsaWtlIGEgdGFiIG9yIHwuCgoxMy4gKipMb29wcyoqIC0gWW91IHVzZSBsb29wcyB3aGVuIHlvdSB3YW50IHRvIHJlcGVhdCBhIHBpZWNlIG9mIGNvZGUgbXVsdGlwbGUgdGltZXMuICBGb3IgZXhhbXBsZSwgbGF0ZXIgaW4gdGhlIGNvdXJzZSB3ZSB3aWxsIHJ1biBuZXR3b3JrIHNpbXVsYXRpb25zIHdoZXJlIHdlIHdhbnQgdG8gc2VlIGhvdyBhbiBvdXRjb21lIGRpZmZ1c2VzIG92ZXIgdGltZTsgZWFjaCB1bml0IG9mIHRpbWUgd2lsbCBjb3JyZXNwb25kIHRvIG9uZSBpdGVyYXRpb24gb2YgdGhlIGxvb3AuICBPciB5b3UgbWF5IGhhdmUgYSBsaXN0IG9mIGZpbGVzIHRvIHJlYWQsIHNvIHlvdSB3aWxsIGxvb3Agb3ZlciB0aGF0IGxpc3QgaW4gb3JkZXIgdG8gbG9hZCB0aGVtIG9uZSBhdCBhIHRpbWUuICBXaGlsZSBpdCBpcyBiZXN0IHRvIHVzZSB2ZWN0b3Igb3BlcmF0aW9ucyB3aGVuZXZlciBwb3NzaWJsZSwgbG9vcHMgY2FuIGJlIG1vcmUgaW50dWl0aXZlLiAgCgo=