Set up

library(igraph)
## 
## Attaching package: 'igraph'
## The following objects are masked from 'package:stats':
## 
##     decompose, spectrum
## The following object is masked from 'package:base':
## 
##     union
load('data/example-data.RData')

There should be 4 objects loaded into your R workspace: - pnet_edgelist - pnet_adjmat - snet_edgelist - snet_adjmat

pnet refers to a section of the phonological network first described by Vitevitch (2008). The nodes represent English words, and edges connect words that are phonological neighbors of each other based on the 1-edit distance metric computed on their phonological transcriptions (Luce & Pisoni, 1998). E.g., //–// are neighboring nodes. Specifically, this network is the 2-hop network of the word ‘speech’ - in addition to ‘speech’ itself, its immediate phonological neighbors and the neighbors of its neighbors are included in this representation.

snet refers to a section of the word association network using data from De Deyne et al. (2020). The nodes represent English words, and edges connect words that are produced as free associations of other words. E.g., “cat”–“dog” are neighboring nodes. This network has edges with 2 interesting properties. (i) Each edge has a weight attribute that corresponds to the associative strength of two nodes, or the proportion of participants who provided a specific response to the cue word. (ii) The edges are also directed such the direction goes from the cue word to the response word, i.e., “cat”->“dog”. Specifically, this network is the 1-hop network of the word ‘cheese’ - in addition to ‘cheese’ itself, its immediate associates and the cue words that led to the response ‘cheese’ are included in this representation. See also https://smallworldofwords.org/en/project/research for more information.

edgelist refers to a type of network data representation where each row represents a single edge in the network with at least 2 columns where the labels of connected nodes are provided. The number of rows in the edge list corresponds to the number of edges in the network. Additional columns can be specified that provide more information about the edges (e.g., type, weight).

adjmat refers to a type of network data representation known as an adjacency matrix where the edge connectivity is represented in the matrix. The number of rows and columns correspond to the number of nodes in the network. A non-zero value in the [i,j] element of the adjacency matrix indicates the presence of a link between node i and node j.

head(pnet_edgelist, 3)
##      [,1]        [,2]       
## [1,] "biC;beach" "iC;each"  
## [2,] "biC;beach" "liC;leach"
## [3,] "iC;each"   "liC;leach"
head(snet_edgelist, 3)
# pnet_adjmat[1:5, 1:5] 

# snet_adjmat[1:5, 1:5]

Creating a network from your data

Example 1: Phonological network with undirected and unweighted edges

From an adjacency matrix

g_pnet_adjmat <- graph_from_adjacency_matrix(
  adjmatrix = pnet_adjmat, 
  mode = 'undirected', 
  weighted = NULL
  )
## Loading required package: Matrix
summary(g_pnet_adjmat)
## IGRAPH d70160e UN-- 39 121 -- 
## + attr: name (v/c)

From an edge list

g_pnet_edgelist <- graph_from_edgelist(
  el = pnet_edgelist, 
  directed = FALSE
  )

summary(g_pnet_edgelist)
## IGRAPH 8bfc1dd UN-- 39 121 -- 
## + attr: name (v/c)

Example 2: Semantic network with directed and weighted edges

From an adjacency matrix

g_snet_adjmat <- graph_from_adjacency_matrix(
  adjmatrix = snet_adjmat, 
  mode = 'directed', 
  weighted = 'weight'
  )

summary(g_snet_adjmat)
## IGRAPH a8f5532 DNW- 211 1332 -- 
## + attr: name (v/c), weight (e/n)

Note that the direction of edges in directed networks follows this convention: ‘From’ = rows; ‘To’ = columns

Here is an example:

snet_adjmat[1:5, 1:5] # from 'age' to 'aging'
## 5 x 5 sparse Matrix of class "dgCMatrix"
##          age aged ages aging american
## age        .    .    .  0.02     .   
## aged       .    .    .  .        .   
## ages       .    .    .  .        .   
## aging      .    .    .  .        .   
## american   .    .    .  .        0.02
E(g_snet_adjmat) |> head() # '|>' pipes output from one function into another 
## + 6/1332 edges from a8f5532 (vertex names):
## [1] mature  ->age   maturity->age   mature  ->aged  maturity->aged 
## [5] age     ->aging maturity->aging

From an edge list

g_snet_edgelist <- graph_from_data_frame(
  d = snet_edgelist, 
  directed = TRUE, 
  vertices = NULL
  )

summary(g_snet_edgelist)
## IGRAPH 4673698 DNW- 211 1332 -- 
## + attr: name (v/c), weight (e/n)

The column containing the edge weights must be labelled ‘weight’ in the data frame. Note that we used the graph_from_data_frame function here instead of the graph_from_edgelist function, as the latter does not have an argument to specify the edge weights.

Measuring the network

Once we have a network representation, the tools of network science can be applied to analyze the networks in different ways. In this tutorial we focus on a descriptive analysis of the network and review various network measures that can be used to describe or quantify network structure at three different levels of the network: the micro-level (referring to the local structure and other properties of individual nodes), the meso-level (subgroups or clusters of nodes), and the macro-level (referring to the overall or global structure of the network).

Micro-level (node-level)

Micro-level network measures provide you with information about specific nodes in the network. These are generally known as centrality measures in the network science literature. Centrality is the network scientist’s way of quantifying the relative “importance” of a given node relative to other nodes in the network. There are many different definitions of what counts as “central”, as you will see in the following subsections. There is no single “correct” or “best” metric - which metrics are most useful to you will depend on the nature of the system that you are modeling as well as the network behavior that you interested in.

Degree (unweighted edges)

The degree of node i refers to the number of edges or links connected to that node.

If your network has directed edges, in-degree refers to the number of edges that are going towards the target node, whereas out-degree refers to the number of edges that are going away from the target node.

# undirected network
degree(graph = g_pnet_adjmat) # for all nodes in the network
##  xpik;apeak   biC;beach    sid;cede     iC;each   liC;leach        pi;p 
##           2           5           4           5           5           9 
##   p@C;patch   pis;peace   piC;peach    pik;peak    pil;peal   piz;pease 
##           6           9          22          12           9           9 
##    pit;peat    pin;peen    pip;peep   piv;peeve   pRC;perch   pIC;pitch 
##           9           9           9           9           6           6 
##   poC;poach   pWC;pouch priC;preach  pUC;putsch   riC;reach    sik;seek 
##           6           6           2           6           6           5 
##  slik;sleek  snik;sneak  sped;spade  spik;speak  spEk;speck   spEd;sped 
##           3           3           3          11           5           4 
## spiC;speech  spid;speed  spYk;spike  spok;spoke  spuk;spook   sp^d;spud 
##           3           8           4           4           4           3 
##  stid;steed  swid;swede   tiC;teach 
##           3           3           5
degree(graph = g_snet_edgelist, v = 'cheese') # for a specific node in the network 
## cheese 
##    231
# directed network
degree(graph = g_snet_adjmat, v = 'cheese', mode = 'in') # in-degree = incoming edges
## cheese 
##    185
degree(graph = g_snet_adjmat, v = 'cheese', mode = 'out') # out-degree = outgoing edges 
## cheese 
##     46
degree(graph = g_snet_adjmat, v = 'cheese', mode = 'all') # in-degree + out-degree
## cheese 
##    231

Strength (weighted edges)

The strength of node i refers to the sum of its adjacent edge weights. Only applicable to weighted networks.

strength(graph = g_snet_adjmat) |> head(5)
##      age     aged     ages    aging american 
##     0.57     0.55     0.04     0.17     0.40
# for directed networks
strength(graph = g_snet_adjmat, v = 'age', mode = 'in') # in-degree = incoming edges
##  age 
## 0.38
strength(graph = g_snet_adjmat, v = 'age', mode = 'out') # out-degree = outgoing edges 
##  age 
## 0.19
strength(graph = g_snet_adjmat, v = 'age', mode = 'all') # in-degree + out-degree
##  age 
## 0.57

Local Clustering Coefficient (unweighted)

The local clustering coefficient, C, of node i measures the ratio of the actual number of edges existing among nodes directly connected to the target node i to the number of all possible edges among these nodes.

C ranges from 0 to 1. When C = 0, none of the neighbors of a target node are neighbors of each other. When C = 1, every neighbor is also a neighbor of all the other neighbors of a target word.

You can think of the local clustering coefficient as providing a measure of the level of interconnectivity among the local neighborhood of the node.

Both words have the same number of neighbors, but different local clustering coefficients.

rbind( # use of rbind to combine node labels and their C values 
  V(g_pnet_edgelist)$name, 
  transitivity(graph = g_pnet_edgelist, type = 'local') |> round(3)
     ) 
##      [,1]        [,2]      [,3]        [,4]   [,5]        [,6]       
## [1,] "biC;beach" "iC;each" "liC;leach" "pi;p" "pis;peace" "piC;peach"
## [2,] "1"         "1"       "1"         "1"    "1"         "0.268"    
##      [,7]        [,8]         [,9]       [,10]      [,11]       [,12]     
## [1,] "p@C;patch" "xpik;apeak" "pik;peak" "pil;peal" "piz;pease" "pit;peat"
## [2,] "1"         "1"          "0.576"    "1"        "1"         "1"       
##      [,13]      [,14]      [,15]       [,16]       [,17]       [,18]      
## [1,] "pin;peen" "pip;peep" "piv;peeve" "pRC;perch" "pIC;pitch" "poC;poach"
## [2,] "1"        "1"        "1"         "1"         "1"         "1"        
##      [,19]       [,20]         [,21]        [,22]       [,23]      [,24]     
## [1,] "pWC;pouch" "priC;preach" "pUC;putsch" "riC;reach" "sid;cede" "sik;seek"
## [2,] "1"         "1"           "1"          "0.733"     "0.5"      "0.4"     
##      [,25]        [,26]        [,27]        [,28]        [,29]       
## [1,] "slik;sleek" "snik;sneak" "spik;speak" "spEk;speck" "sped;spade"
## [2,] "1"          "1"          "0.218"      "0.6"        "1"         
##      [,30]       [,31]         [,32]        [,33]        [,34]       
## [1,] "spEd;sped" "spiC;speech" "spid;speed" "spYk;spike" "spok;spoke"
## [2,] "0.5"       "0.333"       "0.25"       "1"          "1"         
##      [,35]        [,36]       [,37]        [,38]        [,39]      
## [1,] "spuk;spook" "sp^d;spud" "stid;steed" "swid;swede" "tiC;teach"
## [2,] "1"          "1"         "1"          "1"          "1"
transitivity(graph = g_pnet_edgelist, type = 'local', vids = 'spik;speak') |> round(3) # for a specific node in the network 
## [1] 0.218

A couple of things to note:

  1. It is important to specify type = local for local clustering coefficients, as compared to the global clustering coefficient of the entire graph (this is a macro-level measure that we will visit later)

  2. Many of these functions contain additional arguments for indicating whether to consider the directionality and weights of the edges. If your graph is undirected and unweighted, these are ignored by default. If your graph is directed and weighted, you can indicate whether to include or exclude this information for the computation of the network measure. There will be examples of this in the following subsections.

Local Clustering Coefficient (weighted)

If you have a weighted network, you can compute local clustering coefficients using Barrat et al.’s (2004) generalization of transitivity to weighted networks by specifying type = 'weighted'. If your network is unweighted, the generalization will return the unweighted C (see example of ‘speak’ below).

# weighted network 
transitivity(graph = g_snet_edgelist, type = 'local', vids = 'cheese') |> round(3) 
## [1] 0.043
transitivity(graph = g_snet_edgelist, type = 'weighted', vids = 'cheese') |> round(3) 
## [1] 0.057
# unweighted network 
transitivity(graph = g_pnet_edgelist, type = 'local', vids = 'spik;speak') |> round(3)
## [1] 0.218
transitivity(graph = g_pnet_edgelist, type = 'weighted', vids = 'spik;speak') |> round(3)
## [1] 0.218

Closeness Centrality

Closeness centrality of node i is the inverse of the average of the length of the shortest path between node i and all other nodes in the network. If a node has high closeness centrality, it means that on average, it takes few steps to travel from that node to all other nodes in the network. If a node has low closeness centrality, it means that on average, it takes more steps to travel from that node to all other nodes in the network.

Closeness centrality is commonly viewed as an indicator of the accessibility of a node in the network from all other locations in the network.

This is a famous network (Krackhardt’s Kite) that nicely illustrates the differences between degree, closeness, and betweenness centrality.

# closeness centralities for directed networks, ignoring weights  
closeness(graph = g_snet_edgelist, normalized = T, mode = 'all', weights = NA) |> head()
##       age      aged      ages     aging  american appetizer 
## 0.5060241 0.5072464 0.5023923 0.5060241 0.5134474 0.5121951
closeness(graph = g_snet_edgelist, normalized = T, mode = 'in', weights = NA) |> head()
##       age      aged      ages     aging  american appetizer 
## 0.6666667 0.6666667       NaN 0.5000000 0.2790698 0.3279743
closeness(graph = g_snet_edgelist, normalized = T, mode = 'out', weights = NA) |> head()
##       age      aged      ages     aging  american appetizer 
## 0.3380282 0.3503650 0.3317422 0.3333333 0.3520408 0.3650794
# weights are considered by default if graph has a weight attribute 
closeness(graph = g_snet_edgelist, normalized = T, mode = 'all') |> head()
##       age      aged      ages     aging  american appetizer 
##  18.46966  14.54294  18.60053  18.48592  19.84877  17.90281
closeness(graph = g_snet_edgelist, normalized = T, mode = 'all', weights = NULL) |> head() # if weights = NULL and there is an edge attribute called weight, this will be used by default 
##       age      aged      ages     aging  american appetizer 
##  18.46966  14.54294  18.60053  18.48592  19.84877  17.90281

Note that closeness centrality can only be meaningfully computed for connected graphs. If there are distinct network components, this means that for some sets of node pairs, the path between them does not exist and closeness cannot be computed. Usually, network scientists focus their analysis on the largest connected component of the network and ignore the smaller connected components (viewed as outliers). In the Appendix there is a section that describes how to check for the presence of network components in your network (i.e., not fully connected) and how to extract the component you want for further analysis.

It is typical to have normalized = T so that the values are normalized with respect to the size of the network. As usual, you can specify the mode and weights arguments accordingly if you have directed/weighted networks to get the corresponding versions of closeness centrality computed. However, caution is needed as the interpretation of weights in this context is to interpret them as distances: higher weights = longer distances (From igraph manual: “If the graph has a weight edge attribute, then this is used by default. Weights are used for calculating weighted shortest paths, so they are interpreted as distances.”). It is highly recommended to read the manual carefully to understand the measures that are being computed.

Betweenness Centrality

Betweenness centrality is a measure of the degree to which nodes stand in between each other. A node with a high betweenness centrality is a node that is frequently found in the short paths of other pairs of nodes in the network. In contrast, a node with a low betweenness centrality is a node that is not usually found in the short paths of node pairs. Betweeenness can be viewed as an indicator if whether a node represents a “bottleneck” in the system.

# undirected, unweighted network 
betweenness(graph = g_pnet_adjmat, normalized = T, weights = NA, directed = F) |> head(10)
## xpik;apeak  biC;beach   sid;cede    iC;each  liC;leach       pi;p  p@C;patch 
## 0.00000000 0.00000000 0.01730678 0.00000000 0.00000000 0.00000000 0.00000000 
##  pis;peace  piC;peach   pik;peak 
## 0.00000000 0.53840683 0.27192982
# directed, weighted network 
betweenness(graph = g_snet_adjmat, normalized = T, weights = NULL, directed = T) |> head(10)
##          age         aged         ages        aging     american    appetizer 
## 0.0002050581 0.0009881305 0.0000000000 0.0001367054 0.0103941672 0.0098454470 
##      artisan    asparagus    aubergine     baguette 
## 0.0000000000 0.0002904990 0.0000000000 0.0148101856

The same considerations (about connected graphs, additional arguments for weighted and directed graphs, normalization, interpretation of weights as distances) from the closeness centrality section applies to this section as well.

Page Rank Centrality

PageRank is a centrality measure developed by Google to rank webpages (the historic paper describing the algorithm can be viewed here. The general idea is that a random walker will traverse the network space and their paths are biased by the link connectivity structure of the network. The random walker restarts the walk after some time (“boredom”). The number of visits received by a node provides an indicator of its importance in the network. Intuitively, we expect that nodes have a high PageRank if there are many nodes that point to it, or if there are nodes that point to it that themselves have a high PageRank.

# undirected, unweighted network 
page_rank(graph = g_pnet_edgelist, directed = F, weights = NA)$vector |> head(10)
##  biC;beach    iC;each  liC;leach       pi;p  pis;peace  piC;peach  p@C;patch 
## 0.02109911 0.02109911 0.02109911 0.02849254 0.02849254 0.07464375 0.02307469 
## xpik;apeak   pik;peak   pil;peal 
## 0.01085805 0.04130385 0.02849254
# directed, weighted network 
page_rank(graph = g_snet_edgelist, directed = T, weights = NULL)$vector |> head(10)
##          age         aged         ages        aging     american    appetizer 
## 0.0016032147 0.0012431529 0.0007544659 0.0009711727 0.0012917020 0.0008032350 
##      artisan    asparagus    aubergine     baguette 
## 0.0007544659 0.0008365034 0.0011033372 0.0015875815

The weights and directed arguments can be adjusted depending on your graph type. It is important to note that the interpretation of edge weights here is that of “connection strength” (from igraph manual: “This function interprets edge weights as connection strengths. In the random surfer model, an edge with a larger weight is more likely to be selected by the surfer.”). This is different from the “distance” interpretation of edge weights by closeness and betweenness.

Meso-level (community structure)

A common feature of many real-world networks is that they have community structure. Nodes are considered to be part of the same community if the density of connections among those nodes is relatively higher than the density of connections between nodes from different communities (Newman, 2006).

Modularity, Q, is a measure of the density of links inside communities in relation to the density of links between communities (Fortunato, 2010). Networks with higher Q are said to show strong evidence of community structure.

Communities are depicted in different colors from another famous network: Zachary’s Karate Club Network

How do network scientists “find” communities in networks?

Many community detection methods have been developed by network scientists to detect communities in networks. It is sort of like a “clustering analysis” for network scientists. Here we will go through four examples that reflect broad classes of community detection techniques. Each differs in their implementation, and reflects the creator’s implicit definition of what is a community.

Note: In this tutorial the community detection is only implemented on g_pnet_adjmat, an undirected and unweighted network. The usual arguments for weights and directed are available in the community detection function if you wish to toggle these on for weighted and directed networks.

Edge betweenness (“divisive method”)

The core idea behind this technique is that edges connecting separate communities tend to have high edge betweenness as all the shortest paths from one community to another must traverse through them.

The algorithm works by calculating the edge betweenness of all edges the graph, removing the edge with the highest edge betweenness score, then recalculating edge betweenness of remaining edges and again removing the one with the highest score. This repeats until modularity cannot be improved further.

# run the community detection algorithm 
results_edge <- cluster_edge_betweenness(graph = g_pnet_adjmat)

# overall results 
modularity(results_edge)
## [1] 0.5622225
sizes(results_edge)
## Community sizes
##  1  2  3  4  5 
##  9  6  8 10  6
# specific community membership for each node 
cbind(
  results_edge$names,
  results_edge$membership
) |> head(5)
##      [,1]         [,2]
## [1,] "xpik;apeak" "1" 
## [2,] "biC;beach"  "2" 
## [3,] "sid;cede"   "3" 
## [4,] "iC;each"    "2" 
## [5,] "liC;leach"  "2"

Saving the community detection results as a communities object enables the use of special functions like modularity() and sizes() to obtain the modularity of the network and its community sizes. I have also included code that shows how to extract the community memberships of all nodes in the network for further analysis. This applies to the other community detection algorithms as well.

Louvain method (“greedy, maximization method”)

The core idea behind this method is that communities are essentially “mergers” of small communities (Blondel et al., 2008), reflecting the self-similar nature of complex networks.

  1. Each node is assigned to one community such that there are as many communities as there are nodes. Then remove node i from its community and placing it in the community of the neighbor which yields the greatest gain in modularity.
  • repeat for all nodes in the network
  1. A new network is built where nodes are the communities found in the previous phase. Repeat Step 1.
  • repeat Step 1 and 2 until it is not possible to further increase the value of Q
# run the community detection algorithm 
results_louvain <- cluster_louvain(graph = g_pnet_adjmat)

# overall results 
modularity(results_louvain)
## [1] 0.5795028
sizes(results_louvain)
## Community sizes
## 1 2 3 4 5 
## 9 7 8 9 6
# specific community membership for each node 
cbind(
  results_louvain$names,
  results_louvain$membership
) |> head(5)
##      [,1]         [,2]
## [1,] "xpik;apeak" "1" 
## [2,] "biC;beach"  "2" 
## [3,] "sid;cede"   "3" 
## [4,] "iC;each"    "2" 
## [5,] "liC;leach"  "2"

Random walker (“dynamic method”)

The core idea behind this method is that if there are communities in the network, a random walker will tend to spend more time inside the community than outside.

The Walktrap algorithm groups nodes together based on the similarities of the paths taken by the random walker starting from that node. The idea is to merge sets of vertices that have low “distance” from each other.

# run the community detection algorithm 
results_walktrap <- cluster_walktrap(graph = g_pnet_adjmat)

# overall results 
modularity(results_walktrap)
## [1] 0.5608906
sizes(results_walktrap)
## Community sizes
##  1  2  3  4  5 
## 10  7 10  6  6
# specific community membership for each node 
cbind(
  results_walktrap$names,
  results_walktrap$membership
) |> head(5)
##      [,1]         [,2]
## [1,] "xpik;apeak" "1" 
## [2,] "biC;beach"  "4" 
## [3,] "sid;cede"   "2" 
## [4,] "iC;each"    "4" 
## [5,] "liC;leach"  "4"

Infomap (“information-theoretic method”)

The core idea behind this algorithm is to leverage on information-theoretic methods to “describe” the information flow of the entire system (based on random walks).

The Infomap algorithm attempts to describe the random walker’s trajectory using the fewest number of “bits” of information. Communities are groups of nodes that receive new “names” during the compression.

# run the community detection algorithm 
results_infomap <- cluster_infomap(graph = g_pnet_adjmat)

# overall results 
modularity(results_infomap)
## [1] 0.5795028
sizes(results_infomap)
## Community sizes
## 1 2 3 4 5 
## 9 7 8 9 6
# specific community membership for each node 
cbind(
  results_infomap$names,
  results_infomap$membership
) |> head(5)
##      [,1]         [,2]
## [1,] "xpik;apeak" "1" 
## [2,] "biC;beach"  "2" 
## [3,] "sid;cede"   "3" 
## [4,] "iC;each"    "2" 
## [5,] "liC;leach"  "2"

Comparison of methods

Fortunato (2010) summarized papers that conducted a comprehensive comparison of community detection techniques.

Generally, Rosvall & Bergstorm’s Infomap and Blondel et al.’s greedy modularity maximization method performed the best. Both also were relatively fast algorithms.

The code below illustrates the similarities and differences in the results of the various community detection methods.

# comparing the Qs

rbind(
  c('edge_betweenness', 'Louvain', 'Walktrap', 'Infomap'),
  c(modularity(results_edge), modularity(results_louvain), modularity(results_walktrap), modularity(results_infomap)) |> round(3)
)
##      [,1]               [,2]      [,3]       [,4]     
## [1,] "edge_betweenness" "Louvain" "Walktrap" "Infomap"
## [2,] "0.562"            "0.58"    "0.561"    "0.58"
# comparing community membership 

par(mar=c(0,0,0,0)+.6, mfrow = c(2,2)) # reduce margins and plot both networks together

set.seed(1)
fixed_l <- layout_with_fr(g_pnet_adjmat) # to fix node layout across plots 

plot(results_edge, g_pnet_adjmat, layout = fixed_l, main = 'edge betweenness')
plot(results_louvain, g_pnet_adjmat, layout = fixed_l, main = 'Louvain')
plot(results_walktrap, g_pnet_adjmat, layout = fixed_l, main = 'Walktrap')
plot(results_infomap, g_pnet_adjmat, layout = fixed_l, main = 'Infomap')

Macro-level (network-level)

In this section, we will review network science measures that describe the overall or global structure of the entire network. You can think of these measures as providing a “bird’s eye view” of your network, and they are useful for comparing different network representations.

Average Shortest Path Length

Average shortest path length (ASPL) refers to the mean of the shortest possible path between all possible pairs of nodes in the network. (This loosely corresponds to the idea of “six degrees of separation” in social networks.)

Example depicting the shortest path between nodes 25 and 16.

# undirected, unweighted network 
average.path.length(graph = g_pnet_adjmat, weights = NA, directed = F)
## [1] 2.557355
average.path.length(graph = g_pnet_adjmat) # default values for weights and directed give the same values since this is an undirected, unweighted network 
## [1] 2.557355
# an alternative function - both give the same result 
mean_distance(graph = g_pnet_adjmat)
## [1] 2.557355
# directed, weighted network 
mean_distance(graph = g_snet_adjmat, weights = NULL, directed = T)
## [1] 0.1090017
mean_distance(graph = g_snet_adjmat, weights = NULL, directed = F) # ignore direction 
## [1] 0.06239856
mean_distance(graph = g_snet_adjmat, weights = NA, directed = T) # ignore weights 
## [1] 2.926504

Global Clustering Coefficient

Global clustering coefficient refers to the number of closed triangles in the network relative to the number of possible triangles. It is a measure of overall level of local connectivity among nodes in the network.

A simple way of thinking about this concept is that it is measuring the probability that each pair of “friends” of a given node are also friends with each other.

transitivity(graph = g_pnet_adjmat, type = 'global')
## [1] 0.6805869
transitivity(graph = g_snet_adjmat, type = 'global')
## [1] 0.1588113

Small World Index

The term “small world” has a specific meaning in network science as compared to the layperson’s. A network is considered to have small world characteristics if (i) its ASPL is shorter than that of a randomly generated network with the same number of nodes and edges, and (ii) its global C is larger than that of a randomly generated network with the same number of nodes and edges. There are various ways to compute a value that quantifies the “small worldness” of a network, although we do not cover them here (see Humphries and Gurney, 2008, for an example, and Neal, 2017, for a comparison of different methods).

The main take home message is that a small world network has high levels of local clustering (nodes whose neighbors are also neighbors of each other), but there also exists a number of shortcuts that drastically reduces the overall distances/path lengths between nodes. See below for an illustration of this idea.

Network Density

Network density refers to the ratio of the number of (existing) edges and the number of possible edges among nodes in the network.

Simple example of networks with lower and higher network densities.

graph.density(graph = g_pnet_adjmat)
## [1] 0.1632928
graph.density(graph = g_snet_adjmat)
## [1] 0.03006093

Network Diameter

Network diameter refers to length of the longest shortest path between nodes in the network. Instead of getting the mean of all the shortest paths as you did in ASPL, what is the maximum length of those short paths?

Simple example of networks with higher and lower network diameters

# undirected, unweighted graph 
diameter(graph = g_pnet_adjmat, directed = F, weights = NA)
## [1] 4
diameter(graph = g_pnet_adjmat)
## [1] 4
# directed, weighted graph 
diameter(graph = g_snet_adjmat, directed = T, weights = NULL)
## [1] 0.88
diameter(graph = g_snet_adjmat, directed = F, weights = NULL) # ignore direction 
## [1] 0.56
diameter(graph = g_snet_adjmat, directed = T, weights = NA) # ignore weights 
## [1] 7

Appendix: Network Visualization

The purpose of this section is to provide a gentle introduction to network visualization in igraph. Generally, it is advisable to only visualize small networks or a subset of a larger network; this is because it quickly becomes too challenging to develop a meaningful visual representation of a large network with many nodes and edges.

For the purposes of the tutorial we will work with a randomly generated network g. The default plot does not look nice…

par(mar=c(0,0,0,0)+.1) # reduce margins

set.seed(5)
g <- sample_gnp(n = 20, p = 0.20) # 20 nodes with edge probability of 0.2
plot(g)

Node Parameters

This code chunk illustrates a few of the most commonly used node/vertex parameters in visualization.

par(mar=c(0,0,0,0)+.1) # reduce margins

plot(g,
     vertex.color = 'darkorchid1', # change color of nodes 
     vertex.frame.color = 'lightgrey', # change the outline color of nodes 
     vertex.label.dist = 1.7, # adjust distance of node label from node 
     vertex.label.family = 'sans', # change font 
     vertex.size = degree(g) # size of node corresponds to its degree 
     )

Edge Parameters

To illustrate the edge parameters, a weighted and directed network gw is created. The code chunk below illustrates a few of the most commonly used edge parameters in visualization.

set.seed(9)
gw <- sample_gnp(n = 20, p = 0.20, directed = T) # 20 nodes with edge probability of 0.2, edges are directed
E(gw)$weight <- sample(1:5, size = gsize(gw), replace = T) # randomly add edge weights of 1 to 5 
summary(gw) # the 'DW' indicates a directed and weighted network 
## IGRAPH dd949bb D-W- 20 72 -- Erdos-Renyi (gnp) graph
## + attr: name (g/c), type (g/c), loops (g/l), p (g/n), weight (e/n)
par(mar=c(0,0,0,0)+.1) # reduce margins

plot(gw,
     edge.color = 'darkolivegreen', # color of edges 
     edge.width = E(gw)$weight, # the width of edges corresponds to the edge weight 
     edge.curved = 0.5, # add curvature to edges 
     edge.arrow.width = 0.5, # adjust arrow width
     edge.arrow.size = 0.8 # adjust arrow size 
     )

Network Layouts

You can also adjust the overall layout of the network. These layouts are different network visualization approaches that use various algorithms to decide how nodes should be best positioned on a 2D plane, while considering the nature of their edge connectivity. There are many different layouts available - you can either check out the igraph manual or check out this online tutorial (https://kateto.net/network-visualization) for inspiration.

par(mar=c(0,0,0,0)+.4, mfrow = c(1,2)) # reduce margins and plot both networks together

set.seed(1) 

plot(g, layout = layout_in_circle, main = 'circle')
plot(g, layout = layout_with_gem, main = 'gem')

Appendix: Network Components

In this section, the goal is to introduce useful R code for (i) detecting if your network comprises of a single connected component or multiple, and (ii) extracting the largest connected component of the network (or another network component) as a new graph object for additional analysis.

How many components does my network have?

par(mar=c(0,0,0,0)+.1) # reduce margins

set.seed(88)
gz <- sample_gnp(n = 20, p = 0.10)
plot(gz)

gz_comp <- components(gz)

gz_comp$membership # component membership 
##  [1] 1 1 1 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 3 1
gz_comp$csize # component size 
## [1] 18  1  1
gz_comp$no # number of components 
## [1] 3

How can I extract a specific network component as a new network object?

We can use the induced_subgraph function to create “subsets” of a network by selecting the nodes that you wish to keep. These nodes and all the edges among them will be retained in the new network object.

par(mar=c(0,0,0,0)+.1) # reduce margins

# gz_comp <- components(gz)
gz_lcc <- induced_subgraph(graph = gz, 
                           vids = gz_comp$membership == which.max(gz_comp$csize) # a T/F vector indicating the nodes whose component membership is the same as the largest component - we can get this information from the components object above
                           )

plot(gz_lcc)

# you can specify any component size you wish
gz_hermit <- induced_subgraph(graph = gz, 
                              vids = gz_comp$membership == 3 
                              )

plot(gz_hermit)

Additional Resources

Ognyanova, K. (2021) Network visualization with R. Retrieved from www.kateto.net/network-visualization. https://kateto.net/network-visualization

The official igraph manual (v.1.3.4). https://igraph.org/r/doc/

Gephi: A multi-platform, free to download GUI app for network analysis and visualization. https://gephi.org/

References

Barrat, A., Barthélemy, M., Pastor-Satorras, R., & Vespignani, A. (2004). The architecture of complex weighted networks. Proceedings of the National Academy of Sciences, 101(11), 3747–3752. https://doi.org/10.1073/pnas.0400087101

Blondel, V. D., Guillaume, J. L., Lambiotte, R., & Lefebvre, E. (2008). Fast unfolding of communities in large networks. Journal of Statistical Mechanics: Theory and Experiment, 2008(10), P10008.

De Deyne, S., Navarro, D. J., Perfors, A., Brysbaert, M., & Storms, G. (2019). The “Small World of Words” English word association norms for over 12,000 cue words. Behavior Research Methods, 51, 987–1006.

Fortunato, S. (2010). Community detection in graphs. Physics Reports, 486(3-5), 75-174.

Girvan, M., & Newman, M. E. (2002). Community structure in social and biological networks. Proceedings of the National Academy of Sciences, 99(12), 7821-7826.

Humphries, M. D., & Gurney, K. (2008). Network ‘small-world-ness’: A quantitative method for determining canonical network equivalence. PloS One, 3(4).

Luce, P. A., & Pisoni, D. B. (1998). Recognizing spoken words: The Neighborhood Activation Model. Ear and Hearing, 19(1), 1–36.

Neal, Z. P. (2017). How small is it? Comparing indices of small worldliness. Network Science, 5(1), 30–44. https://doi.org/10.1017/nws.2017.5

Newman, M. E. (2006). Modularity and community structure in networks. Proceedings of the National Academy of Sciences, 103(23), 8577-8582.

Pons, P., & Latapy, M. (2005, October). Computing communities in large networks using random walks. In International symposium on computer and information sciences (pp. 284-293). Springer, Berlin, Heidelberg.

Vitevitch, M. S. (2008). What can graph theory tell us about word learning and lexical retrieval? Journal of Speech, Language, and Hearing Research, 51(2), 408–422. https://doi.org/10.1044/1092-4388(2008/030)

LS0tCnRpdGxlOiAiVlBGIE5ldFNjaSBUdXRvcmlhbCIKc3VidGl0bGU6ICJQYXJ0IDI6IERlbW9uc3RyYXRpb24iCmF1dGhvcjogIkN5bnRoaWEgU2lldyIKZGF0ZTogIjcvMjUvMjAyMiIKb3V0cHV0OgogIGh0bWxfZG9jdW1lbnQ6CiAgICB0b2M6IFRSVUUKICAgIHRvY19mbG9hdDogVFJVRQogICAgZGZfcHJpbnQ6IHBhZ2VkCiAgICBjb2RlX2Rvd25sb2FkOiB0cnVlCi0tLQojIFNldCB1cCAKCmBgYHtyIHNldC11cH0KbGlicmFyeShpZ3JhcGgpCgpsb2FkKCdkYXRhL2V4YW1wbGUtZGF0YS5SRGF0YScpCmBgYAoKVGhlcmUgc2hvdWxkIGJlIDQgb2JqZWN0cyBsb2FkZWQgaW50byB5b3VyIFIgd29ya3NwYWNlOgotIGBwbmV0X2VkZ2VsaXN0YAotIGBwbmV0X2Fkam1hdGAKLSBgc25ldF9lZGdlbGlzdGAKLSBgc25ldF9hZGptYXRgIAoKYHBuZXRgIHJlZmVycyB0byBhIHNlY3Rpb24gb2YgdGhlIHBob25vbG9naWNhbCBuZXR3b3JrIGZpcnN0IGRlc2NyaWJlZCBieSBWaXRldml0Y2ggKDIwMDgpLiBUaGUgbm9kZXMgcmVwcmVzZW50IEVuZ2xpc2ggd29yZHMsIGFuZCBlZGdlcyBjb25uZWN0IHdvcmRzIHRoYXQgYXJlIHBob25vbG9naWNhbCBuZWlnaGJvcnMgb2YgZWFjaCBvdGhlciBiYXNlZCBvbiB0aGUgMS1lZGl0IGRpc3RhbmNlIG1ldHJpYyBjb21wdXRlZCBvbiB0aGVpciBwaG9ub2xvZ2ljYWwgdHJhbnNjcmlwdGlvbnMgKEx1Y2UgJiBQaXNvbmksIDE5OTgpLiBFLmcuLCAva0B0Ly0tL2tAcC8gYXJlIG5laWdoYm9yaW5nIG5vZGVzLiBTcGVjaWZpY2FsbHksIHRoaXMgbmV0d29yayBpcyB0aGUgMi1ob3AgbmV0d29yayBvZiB0aGUgd29yZCAnc3BlZWNoJyAtIGluIGFkZGl0aW9uIHRvICdzcGVlY2gnIGl0c2VsZiwgaXRzIGltbWVkaWF0ZSBwaG9ub2xvZ2ljYWwgbmVpZ2hib3JzIGFuZCB0aGUgbmVpZ2hib3JzIG9mIGl0cyBuZWlnaGJvcnMgYXJlIGluY2x1ZGVkIGluIHRoaXMgcmVwcmVzZW50YXRpb24uCgpgc25ldGAgcmVmZXJzIHRvIGEgc2VjdGlvbiBvZiB0aGUgd29yZCBhc3NvY2lhdGlvbiBuZXR3b3JrIHVzaW5nIGRhdGEgZnJvbSBEZSBEZXluZSBldCBhbC4gKDIwMjApLiBUaGUgbm9kZXMgcmVwcmVzZW50IEVuZ2xpc2ggd29yZHMsIGFuZCBlZGdlcyBjb25uZWN0IHdvcmRzIHRoYXQgYXJlIHByb2R1Y2VkIGFzIGZyZWUgYXNzb2NpYXRpb25zIG9mIG90aGVyIHdvcmRzLiBFLmcuLCAiY2F0Ii0tImRvZyIgYXJlIG5laWdoYm9yaW5nIG5vZGVzLiBUaGlzIG5ldHdvcmsgaGFzIGVkZ2VzIHdpdGggMiBpbnRlcmVzdGluZyBwcm9wZXJ0aWVzLiAoaSkgRWFjaCBlZGdlIGhhcyBhIGB3ZWlnaHRgIGF0dHJpYnV0ZSB0aGF0IGNvcnJlc3BvbmRzIHRvIHRoZSBhc3NvY2lhdGl2ZSBzdHJlbmd0aCBvZiB0d28gbm9kZXMsIG9yIHRoZSBwcm9wb3J0aW9uIG9mIHBhcnRpY2lwYW50cyB3aG8gcHJvdmlkZWQgYSBzcGVjaWZpYyByZXNwb25zZSB0byB0aGUgY3VlIHdvcmQuIChpaSkgVGhlIGVkZ2VzIGFyZSBhbHNvIGBkaXJlY3RlZGAgc3VjaCB0aGUgZGlyZWN0aW9uIGdvZXMgKmZyb20qIHRoZSBjdWUgd29yZCAqdG8qIHRoZSByZXNwb25zZSB3b3JkLCBpLmUuLCAiY2F0Ii0+ImRvZyIuIFNwZWNpZmljYWxseSwgdGhpcyBuZXR3b3JrIGlzIHRoZSAxLWhvcCBuZXR3b3JrIG9mIHRoZSB3b3JkICdjaGVlc2UnIC0gaW4gYWRkaXRpb24gdG8gJ2NoZWVzZScgaXRzZWxmLCBpdHMgaW1tZWRpYXRlIGFzc29jaWF0ZXMgYW5kIHRoZSBjdWUgd29yZHMgdGhhdCBsZWQgdG8gdGhlIHJlc3BvbnNlICdjaGVlc2UnIGFyZSBpbmNsdWRlZCBpbiB0aGlzIHJlcHJlc2VudGF0aW9uLiBTZWUgYWxzbyBodHRwczovL3NtYWxsd29ybGRvZndvcmRzLm9yZy9lbi9wcm9qZWN0L3Jlc2VhcmNoIGZvciBtb3JlIGluZm9ybWF0aW9uLiAKCmBlZGdlbGlzdGAgcmVmZXJzIHRvIGEgdHlwZSBvZiBuZXR3b3JrIGRhdGEgcmVwcmVzZW50YXRpb24gd2hlcmUgZWFjaCByb3cgcmVwcmVzZW50cyBhIHNpbmdsZSBlZGdlIGluIHRoZSBuZXR3b3JrIHdpdGggYXQgbGVhc3QgMiBjb2x1bW5zIHdoZXJlIHRoZSBsYWJlbHMgb2YgY29ubmVjdGVkIG5vZGVzIGFyZSBwcm92aWRlZC4gVGhlIG51bWJlciBvZiByb3dzIGluIHRoZSBlZGdlIGxpc3QgY29ycmVzcG9uZHMgdG8gdGhlIG51bWJlciBvZiBlZGdlcyBpbiB0aGUgbmV0d29yay4gQWRkaXRpb25hbCBjb2x1bW5zIGNhbiBiZSBzcGVjaWZpZWQgdGhhdCBwcm92aWRlIG1vcmUgaW5mb3JtYXRpb24gYWJvdXQgdGhlIGVkZ2VzIChlLmcuLCB0eXBlLCB3ZWlnaHQpLiAKCmBhZGptYXRgIHJlZmVycyB0byBhIHR5cGUgb2YgbmV0d29yayBkYXRhIHJlcHJlc2VudGF0aW9uIGtub3duIGFzIGFuIGFkamFjZW5jeSBtYXRyaXggd2hlcmUgdGhlIGVkZ2UgY29ubmVjdGl2aXR5IGlzIHJlcHJlc2VudGVkIGluIHRoZSBtYXRyaXguIFRoZSBudW1iZXIgb2Ygcm93cyBhbmQgY29sdW1ucyBjb3JyZXNwb25kIHRvIHRoZSBudW1iZXIgb2Ygbm9kZXMgaW4gdGhlIG5ldHdvcmsuIEEgbm9uLXplcm8gdmFsdWUgaW4gdGhlIFtpLGpdIGVsZW1lbnQgb2YgdGhlIGFkamFjZW5jeSBtYXRyaXggaW5kaWNhdGVzIHRoZSBwcmVzZW5jZSBvZiBhIGxpbmsgYmV0d2VlbiBub2RlICppKiBhbmQgbm9kZSAqaiouIAoKYGBge3IgcHJldmlld30KaGVhZChwbmV0X2VkZ2VsaXN0LCAzKQoKaGVhZChzbmV0X2VkZ2VsaXN0LCAzKQoKIyBwbmV0X2Fkam1hdFsxOjUsIDE6NV0gCgojIHNuZXRfYWRqbWF0WzE6NSwgMTo1XQpgYGAKCiMgQ3JlYXRpbmcgYSBuZXR3b3JrIGZyb20geW91ciBkYXRhIAoKIyMgRXhhbXBsZSAxOiBQaG9ub2xvZ2ljYWwgbmV0d29yayB3aXRoICp1bmRpcmVjdGVkKiBhbmQgKnVud2VpZ2h0ZWQqIGVkZ2VzIAoKIyMjIEZyb20gYW4gYWRqYWNlbmN5IG1hdHJpeCAKCmBgYHtyIHBuZXQtYWRqbWF0fQpnX3BuZXRfYWRqbWF0IDwtIGdyYXBoX2Zyb21fYWRqYWNlbmN5X21hdHJpeCgKICBhZGptYXRyaXggPSBwbmV0X2Fkam1hdCwgCiAgbW9kZSA9ICd1bmRpcmVjdGVkJywgCiAgd2VpZ2h0ZWQgPSBOVUxMCiAgKQoKc3VtbWFyeShnX3BuZXRfYWRqbWF0KQpgYGAKCiMjIyBGcm9tIGFuIGVkZ2UgbGlzdCAKCmBgYHtyIHBuZXQtZWRnZWxpc3R9CmdfcG5ldF9lZGdlbGlzdCA8LSBncmFwaF9mcm9tX2VkZ2VsaXN0KAogIGVsID0gcG5ldF9lZGdlbGlzdCwgCiAgZGlyZWN0ZWQgPSBGQUxTRQogICkKCnN1bW1hcnkoZ19wbmV0X2VkZ2VsaXN0KQpgYGAKCiMjIEV4YW1wbGUgMjogU2VtYW50aWMgbmV0d29yayB3aXRoICpkaXJlY3RlZCogYW5kICp3ZWlnaHRlZCogZWRnZXMgCgojIyMgRnJvbSBhbiBhZGphY2VuY3kgbWF0cml4IAoKYGBge3Igc25ldC1hZGptYXR9Cmdfc25ldF9hZGptYXQgPC0gZ3JhcGhfZnJvbV9hZGphY2VuY3lfbWF0cml4KAogIGFkam1hdHJpeCA9IHNuZXRfYWRqbWF0LCAKICBtb2RlID0gJ2RpcmVjdGVkJywgCiAgd2VpZ2h0ZWQgPSAnd2VpZ2h0JwogICkKCnN1bW1hcnkoZ19zbmV0X2Fkam1hdCkKYGBgCgpOb3RlIHRoYXQgdGhlIGRpcmVjdGlvbiBvZiBlZGdlcyBpbiBkaXJlY3RlZCBuZXR3b3JrcyBmb2xsb3dzIHRoaXMgY29udmVudGlvbjogJ0Zyb20nID0gcm93czsgJ1RvJyA9IGNvbHVtbnMgCgpIZXJlIGlzIGFuIGV4YW1wbGU6IAoKYGBge3J9CnNuZXRfYWRqbWF0WzE6NSwgMTo1XSAjIGZyb20gJ2FnZScgdG8gJ2FnaW5nJwoKRShnX3NuZXRfYWRqbWF0KSB8PiBoZWFkKCkgIyAnfD4nIHBpcGVzIG91dHB1dCBmcm9tIG9uZSBmdW5jdGlvbiBpbnRvIGFub3RoZXIgCmBgYAoKIyMjIEZyb20gYW4gZWRnZSBsaXN0IAoKYGBge3Igc25ldC1lZGdlbGlzdH0KZ19zbmV0X2VkZ2VsaXN0IDwtIGdyYXBoX2Zyb21fZGF0YV9mcmFtZSgKICBkID0gc25ldF9lZGdlbGlzdCwgCiAgZGlyZWN0ZWQgPSBUUlVFLCAKICB2ZXJ0aWNlcyA9IE5VTEwKICApCgpzdW1tYXJ5KGdfc25ldF9lZGdlbGlzdCkKYGBgCgpUaGUgY29sdW1uIGNvbnRhaW5pbmcgdGhlIGVkZ2Ugd2VpZ2h0cyBtdXN0IGJlIGxhYmVsbGVkICd3ZWlnaHQnIGluIHRoZSBkYXRhIGZyYW1lLiBOb3RlIHRoYXQgd2UgdXNlZCB0aGUgYGdyYXBoX2Zyb21fZGF0YV9mcmFtZWAgZnVuY3Rpb24gaGVyZSBpbnN0ZWFkIG9mIHRoZSBgZ3JhcGhfZnJvbV9lZGdlbGlzdGAgZnVuY3Rpb24sIGFzIHRoZSBsYXR0ZXIgZG9lcyBub3QgaGF2ZSBhbiBhcmd1bWVudCB0byBzcGVjaWZ5IHRoZSBlZGdlIHdlaWdodHMuICAKCiMgTWVhc3VyaW5nIHRoZSBuZXR3b3JrIAoKT25jZSB3ZSBoYXZlIGEgbmV0d29yayByZXByZXNlbnRhdGlvbiwgdGhlIHRvb2xzIG9mIG5ldHdvcmsgc2NpZW5jZSBjYW4gYmUgYXBwbGllZCB0byBhbmFseXplIHRoZSBuZXR3b3JrcyBpbiBkaWZmZXJlbnQgd2F5cy4gSW4gdGhpcyB0dXRvcmlhbCB3ZSBmb2N1cyBvbiBhICpkZXNjcmlwdGl2ZSogYW5hbHlzaXMgb2YgdGhlIG5ldHdvcmsgYW5kIHJldmlldyB2YXJpb3VzIG5ldHdvcmsgbWVhc3VyZXMgdGhhdCBjYW4gYmUgdXNlZCB0byBkZXNjcmliZSBvciBxdWFudGlmeSBuZXR3b3JrIHN0cnVjdHVyZSBhdCB0aHJlZSBkaWZmZXJlbnQgbGV2ZWxzIG9mIHRoZSBuZXR3b3JrOiB0aGUgbWljcm8tbGV2ZWwgKHJlZmVycmluZyB0byB0aGUgbG9jYWwgc3RydWN0dXJlIGFuZCBvdGhlciBwcm9wZXJ0aWVzIG9mIGluZGl2aWR1YWwgbm9kZXMpLCB0aGUgbWVzby1sZXZlbCAoc3ViZ3JvdXBzIG9yIGNsdXN0ZXJzIG9mIG5vZGVzKSwgYW5kIHRoZSBtYWNyby1sZXZlbCAocmVmZXJyaW5nIHRvIHRoZSBvdmVyYWxsIG9yIGdsb2JhbCBzdHJ1Y3R1cmUgb2YgdGhlIG5ldHdvcmspLiAKCiFbXShodHRwczovL3d3dy5tZHBpLmNvbS9lZHVjYXRpb24vZWR1Y2F0aW9uLTEwLTAwMTAxL2FydGljbGVfZGVwbG95L2h0bWwvaW1hZ2VzL2VkdWNhdGlvbi0xMC0wMDEwMS1nMDAxLnBuZykKCiMjIE1pY3JvLWxldmVsIChub2RlLWxldmVsKQoKTWljcm8tbGV2ZWwgbmV0d29yayBtZWFzdXJlcyBwcm92aWRlIHlvdSB3aXRoIGluZm9ybWF0aW9uIGFib3V0IHNwZWNpZmljIG5vZGVzIGluIHRoZSBuZXR3b3JrLiBUaGVzZSBhcmUgZ2VuZXJhbGx5IGtub3duIGFzIGNlbnRyYWxpdHkgbWVhc3VyZXMgaW4gdGhlIG5ldHdvcmsgc2NpZW5jZSBsaXRlcmF0dXJlLiBDZW50cmFsaXR5IGlzIHRoZSBuZXR3b3JrIHNjaWVudGlzdCdzIHdheSBvZiBxdWFudGlmeWluZyB0aGUgcmVsYXRpdmUgImltcG9ydGFuY2UiIG9mIGEgZ2l2ZW4gbm9kZSByZWxhdGl2ZSB0byBvdGhlciBub2RlcyBpbiB0aGUgbmV0d29yay4gVGhlcmUgYXJlIFttYW55XShodHRwOi8vc2Nob2NoYXN0aWNzLm5ldC9zbmEvcGVyaW9kaWMuaHRtbCkgZGlmZmVyZW50IGRlZmluaXRpb25zIG9mIHdoYXQgY291bnRzIGFzICJjZW50cmFsIiwgYXMgeW91IHdpbGwgc2VlIGluIHRoZSBmb2xsb3dpbmcgc3Vic2VjdGlvbnMuIFRoZXJlIGlzIG5vIHNpbmdsZSAiY29ycmVjdCIgb3IgImJlc3QiIG1ldHJpYyAtIHdoaWNoIG1ldHJpY3MgYXJlIG1vc3QgdXNlZnVsIHRvIHlvdSB3aWxsIGRlcGVuZCBvbiB0aGUgbmF0dXJlIG9mIHRoZSBzeXN0ZW0gdGhhdCB5b3UgYXJlIG1vZGVsaW5nIGFzIHdlbGwgYXMgdGhlIG5ldHdvcmsgYmVoYXZpb3IgdGhhdCB5b3UgaW50ZXJlc3RlZCBpbi4gCgojIyMgRGVncmVlICh1bndlaWdodGVkIGVkZ2VzKQoKVGhlICoqZGVncmVlKiogb2Ygbm9kZSAqaSogcmVmZXJzIHRvIHRoZSBudW1iZXIgb2YgZWRnZXMgb3IgbGlua3MgY29ubmVjdGVkIHRvIHRoYXQgbm9kZS4KCklmIHlvdXIgbmV0d29yayBoYXMgZGlyZWN0ZWQgZWRnZXMsICppbi1kZWdyZWUqIHJlZmVycyB0byB0aGUgbnVtYmVyIG9mIGVkZ2VzIHRoYXQgYXJlIGdvaW5nIHRvd2FyZHMgdGhlIHRhcmdldCBub2RlLCB3aGVyZWFzICpvdXQtZGVncmVlKiByZWZlcnMgdG8gdGhlIG51bWJlciBvZiBlZGdlcyB0aGF0IGFyZSBnb2luZyBhd2F5IGZyb20gdGhlIHRhcmdldCBub2RlLiAKCiFbXShodHRwczovL3d3dy50bGFiLml0L2VuL2FsbGVnYXRpL2hlbHBfZW5fb25saW5lL3RsYWJfaW1hZ2UvaW5fb3V0X2RlZ3JlZS5qcGcpCgpgYGB7cn0KIyB1bmRpcmVjdGVkIG5ldHdvcmsKZGVncmVlKGdyYXBoID0gZ19wbmV0X2Fkam1hdCkgIyBmb3IgYWxsIG5vZGVzIGluIHRoZSBuZXR3b3JrCgpkZWdyZWUoZ3JhcGggPSBnX3NuZXRfZWRnZWxpc3QsIHYgPSAnY2hlZXNlJykgIyBmb3IgYSBzcGVjaWZpYyBub2RlIGluIHRoZSBuZXR3b3JrIAoKIyBkaXJlY3RlZCBuZXR3b3JrCmRlZ3JlZShncmFwaCA9IGdfc25ldF9hZGptYXQsIHYgPSAnY2hlZXNlJywgbW9kZSA9ICdpbicpICMgaW4tZGVncmVlID0gaW5jb21pbmcgZWRnZXMKZGVncmVlKGdyYXBoID0gZ19zbmV0X2Fkam1hdCwgdiA9ICdjaGVlc2UnLCBtb2RlID0gJ291dCcpICMgb3V0LWRlZ3JlZSA9IG91dGdvaW5nIGVkZ2VzIApkZWdyZWUoZ3JhcGggPSBnX3NuZXRfYWRqbWF0LCB2ID0gJ2NoZWVzZScsIG1vZGUgPSAnYWxsJykgIyBpbi1kZWdyZWUgKyBvdXQtZGVncmVlCmBgYAoKIyMjIFN0cmVuZ3RoICh3ZWlnaHRlZCBlZGdlcykKClRoZSAqKnN0cmVuZ3RoKiogb2Ygbm9kZSAqaSogcmVmZXJzIHRvIHRoZSBzdW0gb2YgaXRzIGFkamFjZW50IGVkZ2UgKndlaWdodHMqLiBPbmx5IGFwcGxpY2FibGUgdG8gd2VpZ2h0ZWQgbmV0d29ya3MuIAoKYGBge3J9CnN0cmVuZ3RoKGdyYXBoID0gZ19zbmV0X2Fkam1hdCkgfD4gaGVhZCg1KQoKIyBmb3IgZGlyZWN0ZWQgbmV0d29ya3MKc3RyZW5ndGgoZ3JhcGggPSBnX3NuZXRfYWRqbWF0LCB2ID0gJ2FnZScsIG1vZGUgPSAnaW4nKSAjIGluLWRlZ3JlZSA9IGluY29taW5nIGVkZ2VzCnN0cmVuZ3RoKGdyYXBoID0gZ19zbmV0X2Fkam1hdCwgdiA9ICdhZ2UnLCBtb2RlID0gJ291dCcpICMgb3V0LWRlZ3JlZSA9IG91dGdvaW5nIGVkZ2VzIApzdHJlbmd0aChncmFwaCA9IGdfc25ldF9hZGptYXQsIHYgPSAnYWdlJywgbW9kZSA9ICdhbGwnKSAjIGluLWRlZ3JlZSArIG91dC1kZWdyZWUKYGBgCgojIyMgTG9jYWwgQ2x1c3RlcmluZyBDb2VmZmljaWVudCAodW53ZWlnaHRlZCkKClRoZSAqKmxvY2FsIGNsdXN0ZXJpbmcgY29lZmZpY2llbnQqKiwgKkMqLCBvZiBub2RlICppKiBtZWFzdXJlcyB0aGUgcmF0aW8gb2YgdGhlIGFjdHVhbCBudW1iZXIgb2YgZWRnZXMgZXhpc3RpbmcgYW1vbmcgbm9kZXMgZGlyZWN0bHkgY29ubmVjdGVkIHRvIHRoZSB0YXJnZXQgbm9kZSAqaSogdG8gdGhlIG51bWJlciBvZiBhbGwgcG9zc2libGUgZWRnZXMgYW1vbmcgdGhlc2Ugbm9kZXMuIAoKKkMqIHJhbmdlcyBmcm9tIDAgdG8gMS4gV2hlbiAqQyogPSAwLCBub25lIG9mIHRoZSBuZWlnaGJvcnMgb2YgYSB0YXJnZXQgbm9kZSBhcmUgbmVpZ2hib3JzIG9mIGVhY2ggb3RoZXIuIFdoZW4gKkMqID0gMSwgZXZlcnkgbmVpZ2hib3IgaXMgYWxzbyBhIG5laWdoYm9yIG9mIGFsbCB0aGUgb3RoZXIgbmVpZ2hib3JzIG9mIGEgdGFyZ2V0IHdvcmQuCgpZb3UgY2FuIHRoaW5rIG9mIHRoZSBsb2NhbCBjbHVzdGVyaW5nIGNvZWZmaWNpZW50IGFzIHByb3ZpZGluZyBhIG1lYXN1cmUgb2YgdGhlIGxldmVsIG9mIGludGVyY29ubmVjdGl2aXR5IGFtb25nIHRoZSBsb2NhbCBuZWlnaGJvcmhvb2Qgb2YgdGhlIG5vZGUuIAoKIVtdKGltZy9oY2MtbGNjLmpwZykKCipCb3RoIHdvcmRzIGhhdmUgdGhlIHNhbWUgbnVtYmVyIG9mIG5laWdoYm9ycywgYnV0IGRpZmZlcmVudCBsb2NhbCBjbHVzdGVyaW5nIGNvZWZmaWNpZW50cy4qCgpgYGB7cn0KcmJpbmQoICMgdXNlIG9mIHJiaW5kIHRvIGNvbWJpbmUgbm9kZSBsYWJlbHMgYW5kIHRoZWlyIEMgdmFsdWVzIAogIFYoZ19wbmV0X2VkZ2VsaXN0KSRuYW1lLCAKICB0cmFuc2l0aXZpdHkoZ3JhcGggPSBnX3BuZXRfZWRnZWxpc3QsIHR5cGUgPSAnbG9jYWwnKSB8PiByb3VuZCgzKQogICAgICkgCgp0cmFuc2l0aXZpdHkoZ3JhcGggPSBnX3BuZXRfZWRnZWxpc3QsIHR5cGUgPSAnbG9jYWwnLCB2aWRzID0gJ3NwaWs7c3BlYWsnKSB8PiByb3VuZCgzKSAjIGZvciBhIHNwZWNpZmljIG5vZGUgaW4gdGhlIG5ldHdvcmsgCmBgYAoKQSBjb3VwbGUgb2YgdGhpbmdzIHRvIG5vdGU6CgoxLiBJdCBpcyBpbXBvcnRhbnQgdG8gc3BlY2lmeSBgdHlwZSA9IGxvY2FsYCBmb3IgbG9jYWwgY2x1c3RlcmluZyBjb2VmZmljaWVudHMsIGFzIGNvbXBhcmVkIHRvIHRoZSBnbG9iYWwgY2x1c3RlcmluZyBjb2VmZmljaWVudCBvZiB0aGUgZW50aXJlIGdyYXBoICh0aGlzIGlzIGEgW21hY3JvLWxldmVsIG1lYXN1cmVdKCNnbG9iYWwtY2x1c3RlcmluZy1jb2VmZmljaWVudCkgdGhhdCB3ZSB3aWxsIHZpc2l0IGxhdGVyKSAKCjIuIE1hbnkgb2YgdGhlc2UgZnVuY3Rpb25zIGNvbnRhaW4gYWRkaXRpb25hbCBhcmd1bWVudHMgZm9yIGluZGljYXRpbmcgd2hldGhlciB0byBjb25zaWRlciB0aGUgZGlyZWN0aW9uYWxpdHkgYW5kIHdlaWdodHMgb2YgdGhlIGVkZ2VzLiBJZiB5b3VyIGdyYXBoIGlzIHVuZGlyZWN0ZWQgYW5kIHVud2VpZ2h0ZWQsIHRoZXNlIGFyZSBpZ25vcmVkIGJ5IGRlZmF1bHQuIElmIHlvdXIgZ3JhcGggaXMgZGlyZWN0ZWQgYW5kIHdlaWdodGVkLCB5b3UgY2FuIGluZGljYXRlIHdoZXRoZXIgdG8gaW5jbHVkZSBvciBleGNsdWRlIHRoaXMgaW5mb3JtYXRpb24gZm9yIHRoZSBjb21wdXRhdGlvbiBvZiB0aGUgbmV0d29yayBtZWFzdXJlLiBUaGVyZSB3aWxsIGJlIGV4YW1wbGVzIG9mIHRoaXMgaW4gdGhlIGZvbGxvd2luZyBzdWJzZWN0aW9ucy4gCgojIyMgTG9jYWwgQ2x1c3RlcmluZyBDb2VmZmljaWVudCAod2VpZ2h0ZWQpCgpJZiB5b3UgaGF2ZSBhIHdlaWdodGVkIG5ldHdvcmssIHlvdSBjYW4gY29tcHV0ZSBsb2NhbCBjbHVzdGVyaW5nIGNvZWZmaWNpZW50cyB1c2luZyBCYXJyYXQgZXQgYWwuJ3MgKDIwMDQpIGdlbmVyYWxpemF0aW9uIG9mIHRyYW5zaXRpdml0eSB0byB3ZWlnaHRlZCBuZXR3b3JrcyBieSBzcGVjaWZ5aW5nIGB0eXBlID0gJ3dlaWdodGVkJ2AuIElmIHlvdXIgbmV0d29yayBpcyB1bndlaWdodGVkLCB0aGUgZ2VuZXJhbGl6YXRpb24gd2lsbCByZXR1cm4gdGhlIHVud2VpZ2h0ZWQgQyAoc2VlIGV4YW1wbGUgb2YgJ3NwZWFrJyBiZWxvdykuIAoKYGBge3J9CiMgd2VpZ2h0ZWQgbmV0d29yayAKdHJhbnNpdGl2aXR5KGdyYXBoID0gZ19zbmV0X2VkZ2VsaXN0LCB0eXBlID0gJ2xvY2FsJywgdmlkcyA9ICdjaGVlc2UnKSB8PiByb3VuZCgzKSAKCnRyYW5zaXRpdml0eShncmFwaCA9IGdfc25ldF9lZGdlbGlzdCwgdHlwZSA9ICd3ZWlnaHRlZCcsIHZpZHMgPSAnY2hlZXNlJykgfD4gcm91bmQoMykgCgojIHVud2VpZ2h0ZWQgbmV0d29yayAKdHJhbnNpdGl2aXR5KGdyYXBoID0gZ19wbmV0X2VkZ2VsaXN0LCB0eXBlID0gJ2xvY2FsJywgdmlkcyA9ICdzcGlrO3NwZWFrJykgfD4gcm91bmQoMykKCnRyYW5zaXRpdml0eShncmFwaCA9IGdfcG5ldF9lZGdlbGlzdCwgdHlwZSA9ICd3ZWlnaHRlZCcsIHZpZHMgPSAnc3BpaztzcGVhaycpIHw+IHJvdW5kKDMpCmBgYAoKIyMjIENsb3NlbmVzcyBDZW50cmFsaXR5IAoKQ2xvc2VuZXNzIGNlbnRyYWxpdHkgb2Ygbm9kZSAqaSogaXMgdGhlIGludmVyc2Ugb2YgdGhlIGF2ZXJhZ2Ugb2YgdGhlIGxlbmd0aCBvZiB0aGUgc2hvcnRlc3QgcGF0aCBiZXR3ZWVuIG5vZGUgKmkqIGFuZCBhbGwgb3RoZXIgbm9kZXMgaW4gdGhlIG5ldHdvcmsuIElmIGEgbm9kZSBoYXMgaGlnaCBjbG9zZW5lc3MgY2VudHJhbGl0eSwgaXQgbWVhbnMgdGhhdCBvbiBhdmVyYWdlLCBpdCB0YWtlcyBmZXcgc3RlcHMgdG8gdHJhdmVsIGZyb20gdGhhdCBub2RlIHRvIGFsbCBvdGhlciBub2RlcyBpbiB0aGUgbmV0d29yay4gSWYgYSBub2RlIGhhcyBsb3cgY2xvc2VuZXNzIGNlbnRyYWxpdHksIGl0IG1lYW5zIHRoYXQgb24gYXZlcmFnZSwgaXQgdGFrZXMgbW9yZSBzdGVwcyB0byB0cmF2ZWwgZnJvbSB0aGF0IG5vZGUgdG8gYWxsIG90aGVyIG5vZGVzIGluIHRoZSBuZXR3b3JrLgoKQ2xvc2VuZXNzIGNlbnRyYWxpdHkgaXMgY29tbW9ubHkgdmlld2VkIGFzIGFuIGluZGljYXRvciBvZiB0aGUgKmFjY2Vzc2liaWxpdHkqIG9mIGEgbm9kZSBpbiB0aGUgbmV0d29yayBmcm9tIGFsbCBvdGhlciBsb2NhdGlvbnMgaW4gdGhlIG5ldHdvcmsuIAoKIVtdKGh0dHBzOi8vd3d3LnJlbGlhbnRzcHJvamVjdC5jb20vd3AtY29udGVudC91cGxvYWRzLzIwMjAvMDYvcmVsaWFudHNfa2V5Y29uY2VwdHMtMDQucG5nKQoqVGhpcyBpcyBhIGZhbW91cyBuZXR3b3JrIChLcmFja2hhcmR0J3MgS2l0ZSkgdGhhdCBuaWNlbHkgaWxsdXN0cmF0ZXMgdGhlIGRpZmZlcmVuY2VzIGJldHdlZW4gZGVncmVlLCBjbG9zZW5lc3MsIGFuZCBiZXR3ZWVubmVzcyBjZW50cmFsaXR5LioKCmBgYHtyfQojIGNsb3NlbmVzcyBjZW50cmFsaXRpZXMgZm9yIGRpcmVjdGVkIG5ldHdvcmtzLCBpZ25vcmluZyB3ZWlnaHRzICAKY2xvc2VuZXNzKGdyYXBoID0gZ19zbmV0X2VkZ2VsaXN0LCBub3JtYWxpemVkID0gVCwgbW9kZSA9ICdhbGwnLCB3ZWlnaHRzID0gTkEpIHw+IGhlYWQoKQpjbG9zZW5lc3MoZ3JhcGggPSBnX3NuZXRfZWRnZWxpc3QsIG5vcm1hbGl6ZWQgPSBULCBtb2RlID0gJ2luJywgd2VpZ2h0cyA9IE5BKSB8PiBoZWFkKCkKY2xvc2VuZXNzKGdyYXBoID0gZ19zbmV0X2VkZ2VsaXN0LCBub3JtYWxpemVkID0gVCwgbW9kZSA9ICdvdXQnLCB3ZWlnaHRzID0gTkEpIHw+IGhlYWQoKQoKIyB3ZWlnaHRzIGFyZSBjb25zaWRlcmVkIGJ5IGRlZmF1bHQgaWYgZ3JhcGggaGFzIGEgd2VpZ2h0IGF0dHJpYnV0ZSAKY2xvc2VuZXNzKGdyYXBoID0gZ19zbmV0X2VkZ2VsaXN0LCBub3JtYWxpemVkID0gVCwgbW9kZSA9ICdhbGwnKSB8PiBoZWFkKCkKY2xvc2VuZXNzKGdyYXBoID0gZ19zbmV0X2VkZ2VsaXN0LCBub3JtYWxpemVkID0gVCwgbW9kZSA9ICdhbGwnLCB3ZWlnaHRzID0gTlVMTCkgfD4gaGVhZCgpICMgaWYgd2VpZ2h0cyA9IE5VTEwgYW5kIHRoZXJlIGlzIGFuIGVkZ2UgYXR0cmlidXRlIGNhbGxlZCB3ZWlnaHQsIHRoaXMgd2lsbCBiZSB1c2VkIGJ5IGRlZmF1bHQgCmBgYAoKTm90ZSB0aGF0IGNsb3NlbmVzcyBjZW50cmFsaXR5IGNhbiBvbmx5IGJlIG1lYW5pbmdmdWxseSBjb21wdXRlZCBmb3IgY29ubmVjdGVkIGdyYXBocy4gSWYgdGhlcmUgYXJlIGRpc3RpbmN0IG5ldHdvcmsgY29tcG9uZW50cywgdGhpcyBtZWFucyB0aGF0IGZvciBzb21lIHNldHMgb2Ygbm9kZSBwYWlycywgdGhlIHBhdGggYmV0d2VlbiB0aGVtIGRvZXMgbm90IGV4aXN0IGFuZCBjbG9zZW5lc3MgY2Fubm90IGJlIGNvbXB1dGVkLiBVc3VhbGx5LCBuZXR3b3JrIHNjaWVudGlzdHMgZm9jdXMgdGhlaXIgYW5hbHlzaXMgb24gdGhlIGxhcmdlc3QgY29ubmVjdGVkIGNvbXBvbmVudCBvZiB0aGUgbmV0d29yayBhbmQgaWdub3JlIHRoZSBzbWFsbGVyIGNvbm5lY3RlZCBjb21wb25lbnRzICh2aWV3ZWQgYXMgb3V0bGllcnMpLiBJbiB0aGUgW0FwcGVuZGl4XSgjQXBwZW5kaXg6X05ldHdvcmtfQ29tcG9uZW50cykgdGhlcmUgaXMgYSBzZWN0aW9uIHRoYXQgZGVzY3JpYmVzIGhvdyB0byBjaGVjayBmb3IgdGhlIHByZXNlbmNlIG9mIG5ldHdvcmsgY29tcG9uZW50cyBpbiB5b3VyIG5ldHdvcmsgKGkuZS4sIG5vdCBmdWxseSBjb25uZWN0ZWQpIGFuZCBob3cgdG8gZXh0cmFjdCB0aGUgY29tcG9uZW50IHlvdSB3YW50IGZvciBmdXJ0aGVyIGFuYWx5c2lzLiAKCkl0IGlzIHR5cGljYWwgdG8gaGF2ZSBgbm9ybWFsaXplZCA9IFRgIHNvIHRoYXQgdGhlIHZhbHVlcyBhcmUgbm9ybWFsaXplZCB3aXRoIHJlc3BlY3QgdG8gdGhlIHNpemUgb2YgdGhlIG5ldHdvcmsuIEFzIHVzdWFsLCB5b3UgY2FuIHNwZWNpZnkgdGhlIGBtb2RlYCBhbmQgYHdlaWdodHNgIGFyZ3VtZW50cyBhY2NvcmRpbmdseSBpZiB5b3UgaGF2ZSBkaXJlY3RlZC93ZWlnaHRlZCBuZXR3b3JrcyB0byBnZXQgdGhlIGNvcnJlc3BvbmRpbmcgdmVyc2lvbnMgb2YgY2xvc2VuZXNzIGNlbnRyYWxpdHkgY29tcHV0ZWQuIEhvd2V2ZXIsIGNhdXRpb24gaXMgbmVlZGVkIGFzIHRoZSBpbnRlcnByZXRhdGlvbiBvZiBgd2VpZ2h0c2AgaW4gdGhpcyBjb250ZXh0IGlzIHRvIGludGVycHJldCB0aGVtIGFzICoqZGlzdGFuY2VzKio6IGhpZ2hlciB3ZWlnaHRzID0gbG9uZ2VyIGRpc3RhbmNlcyAoRnJvbSBgaWdyYXBoYCBtYW51YWw6ICJJZiB0aGUgZ3JhcGggaGFzIGEgd2VpZ2h0IGVkZ2UgYXR0cmlidXRlLCB0aGVuIHRoaXMgaXMgdXNlZCBieSBkZWZhdWx0LiBXZWlnaHRzIGFyZSB1c2VkIGZvciBjYWxjdWxhdGluZyB3ZWlnaHRlZCBzaG9ydGVzdCBwYXRocywgc28gdGhleSBhcmUgaW50ZXJwcmV0ZWQgYXMgZGlzdGFuY2VzLiIpLiBJdCBpcyBoaWdobHkgcmVjb21tZW5kZWQgdG8gcmVhZCB0aGUgbWFudWFsIGNhcmVmdWxseSB0byB1bmRlcnN0YW5kIHRoZSBtZWFzdXJlcyB0aGF0IGFyZSBiZWluZyBjb21wdXRlZC4gCgojIyMgQmV0d2Vlbm5lc3MgQ2VudHJhbGl0eSAKCkJldHdlZW5uZXNzIGNlbnRyYWxpdHkgaXMgYSBtZWFzdXJlIG9mIHRoZSBkZWdyZWUgdG8gd2hpY2ggbm9kZXMgc3RhbmQgaW4gYmV0d2VlbiBlYWNoIG90aGVyLiBBIG5vZGUgd2l0aCBhIGhpZ2ggYmV0d2Vlbm5lc3MgY2VudHJhbGl0eSBpcyBhIG5vZGUgdGhhdCBpcyBmcmVxdWVudGx5IGZvdW5kIGluIHRoZSBzaG9ydCBwYXRocyBvZiBvdGhlciBwYWlycyBvZiBub2RlcyBpbiB0aGUgbmV0d29yay4gSW4gY29udHJhc3QsIGEgbm9kZSB3aXRoIGEgbG93IGJldHdlZW5uZXNzIGNlbnRyYWxpdHkgaXMgYSBub2RlIHRoYXQgaXMgbm90IHVzdWFsbHkgZm91bmQgaW4gdGhlIHNob3J0IHBhdGhzIG9mIG5vZGUgcGFpcnMuIEJldHdlZWVubmVzcyBjYW4gYmUgdmlld2VkIGFzIGFuIGluZGljYXRvciBpZiB3aGV0aGVyIGEgbm9kZSByZXByZXNlbnRzIGEgImJvdHRsZW5lY2siIGluIHRoZSBzeXN0ZW0uIAoKYGBge3J9CiMgdW5kaXJlY3RlZCwgdW53ZWlnaHRlZCBuZXR3b3JrIApiZXR3ZWVubmVzcyhncmFwaCA9IGdfcG5ldF9hZGptYXQsIG5vcm1hbGl6ZWQgPSBULCB3ZWlnaHRzID0gTkEsIGRpcmVjdGVkID0gRikgfD4gaGVhZCgxMCkKCiMgZGlyZWN0ZWQsIHdlaWdodGVkIG5ldHdvcmsgCmJldHdlZW5uZXNzKGdyYXBoID0gZ19zbmV0X2Fkam1hdCwgbm9ybWFsaXplZCA9IFQsIHdlaWdodHMgPSBOVUxMLCBkaXJlY3RlZCA9IFQpIHw+IGhlYWQoMTApCmBgYAoKVGhlIHNhbWUgY29uc2lkZXJhdGlvbnMgKGFib3V0IGNvbm5lY3RlZCBncmFwaHMsIGFkZGl0aW9uYWwgYXJndW1lbnRzIGZvciB3ZWlnaHRlZCBhbmQgZGlyZWN0ZWQgZ3JhcGhzLCBub3JtYWxpemF0aW9uLCBpbnRlcnByZXRhdGlvbiBvZiB3ZWlnaHRzIGFzIGRpc3RhbmNlcykgZnJvbSB0aGUgY2xvc2VuZXNzIGNlbnRyYWxpdHkgc2VjdGlvbiBhcHBsaWVzIHRvIHRoaXMgc2VjdGlvbiBhcyB3ZWxsLiAKCiMjIyBQYWdlIFJhbmsgQ2VudHJhbGl0eQoKUGFnZVJhbmsgaXMgYSBjZW50cmFsaXR5IG1lYXN1cmUgZGV2ZWxvcGVkIGJ5IEdvb2dsZSB0byByYW5rIHdlYnBhZ2VzICh0aGUgaGlzdG9yaWMgcGFwZXIgZGVzY3JpYmluZyB0aGUgYWxnb3JpdGhtIGNhbiBiZSB2aWV3ZWQgW2hlcmVdKGh0dHA6Ly9pbmZvbGFiLnN0YW5mb3JkLmVkdS9+YmFja3J1Yi9nb29nbGUuaHRtbCkuIFRoZSBnZW5lcmFsIGlkZWEgaXMgdGhhdCBhIHJhbmRvbSB3YWxrZXIgd2lsbCB0cmF2ZXJzZSB0aGUgbmV0d29yayBzcGFjZSBhbmQgdGhlaXIgcGF0aHMgYXJlIGJpYXNlZCBieSB0aGUgbGluayBjb25uZWN0aXZpdHkgc3RydWN0dXJlIG9mIHRoZSBuZXR3b3JrLiBUaGUgcmFuZG9tIHdhbGtlciByZXN0YXJ0cyB0aGUgd2FsayBhZnRlciBzb21lIHRpbWUgKCJib3JlZG9tIikuIFRoZSBudW1iZXIgb2YgdmlzaXRzIHJlY2VpdmVkIGJ5IGEgbm9kZSBwcm92aWRlcyBhbiBpbmRpY2F0b3Igb2YgaXRzIGltcG9ydGFuY2UgaW4gdGhlIG5ldHdvcmsuIEludHVpdGl2ZWx5LCB3ZSBleHBlY3QgdGhhdCBub2RlcyBoYXZlIGEgaGlnaCBQYWdlUmFuayBpZiB0aGVyZSBhcmUgbWFueSBub2RlcyB0aGF0IHBvaW50IHRvIGl0LCBvciBpZiB0aGVyZSBhcmUgbm9kZXMgdGhhdCBwb2ludCB0byBpdCB0aGF0IHRoZW1zZWx2ZXMgaGF2ZSBhIGhpZ2ggUGFnZVJhbmsuIAoKYGBge3J9CiMgdW5kaXJlY3RlZCwgdW53ZWlnaHRlZCBuZXR3b3JrIApwYWdlX3JhbmsoZ3JhcGggPSBnX3BuZXRfZWRnZWxpc3QsIGRpcmVjdGVkID0gRiwgd2VpZ2h0cyA9IE5BKSR2ZWN0b3IgfD4gaGVhZCgxMCkKCiMgZGlyZWN0ZWQsIHdlaWdodGVkIG5ldHdvcmsgCnBhZ2VfcmFuayhncmFwaCA9IGdfc25ldF9lZGdlbGlzdCwgZGlyZWN0ZWQgPSBULCB3ZWlnaHRzID0gTlVMTCkkdmVjdG9yIHw+IGhlYWQoMTApCmBgYAoKVGhlIGB3ZWlnaHRzYCBhbmQgYGRpcmVjdGVkYCBhcmd1bWVudHMgY2FuIGJlIGFkanVzdGVkIGRlcGVuZGluZyBvbiB5b3VyIGdyYXBoIHR5cGUuIEl0IGlzIGltcG9ydGFudCB0byBub3RlIHRoYXQgdGhlIGludGVycHJldGF0aW9uIG9mIGVkZ2Ugd2VpZ2h0cyBoZXJlIGlzIHRoYXQgb2YgImNvbm5lY3Rpb24gc3RyZW5ndGgiIChmcm9tIGBpZ3JhcGhgIG1hbnVhbDogIlRoaXMgZnVuY3Rpb24gaW50ZXJwcmV0cyBlZGdlIHdlaWdodHMgYXMgY29ubmVjdGlvbiBzdHJlbmd0aHMuIEluIHRoZSByYW5kb20gc3VyZmVyIG1vZGVsLCBhbiBlZGdlIHdpdGggYSBsYXJnZXIgd2VpZ2h0IGlzIG1vcmUgbGlrZWx5IHRvIGJlIHNlbGVjdGVkIGJ5IHRoZSBzdXJmZXIuIikuIFRoaXMgaXMgZGlmZmVyZW50IGZyb20gdGhlICJkaXN0YW5jZSIgaW50ZXJwcmV0YXRpb24gb2YgZWRnZSB3ZWlnaHRzIGJ5IGNsb3NlbmVzcyBhbmQgYmV0d2Vlbm5lc3MuIAoKIyMgTWVzby1sZXZlbCAoY29tbXVuaXR5IHN0cnVjdHVyZSkKCkEgY29tbW9uIGZlYXR1cmUgb2YgbWFueSByZWFsLXdvcmxkIG5ldHdvcmtzIGlzIHRoYXQgdGhleSBoYXZlICoqY29tbXVuaXR5IHN0cnVjdHVyZSoqLiBOb2RlcyBhcmUgY29uc2lkZXJlZCB0byBiZSBwYXJ0IG9mIHRoZSBzYW1lIGNvbW11bml0eSBpZiB0aGUgZGVuc2l0eSBvZiBjb25uZWN0aW9ucyBhbW9uZyB0aG9zZSBub2RlcyBpcyByZWxhdGl2ZWx5IGhpZ2hlciB0aGFuIHRoZSBkZW5zaXR5IG9mIGNvbm5lY3Rpb25zIGJldHdlZW4gbm9kZXMgZnJvbSBkaWZmZXJlbnQgY29tbXVuaXRpZXMgKE5ld21hbiwgMjAwNikuCgoqKk1vZHVsYXJpdHksIFEqKiwgaXMgYSBtZWFzdXJlIG9mIHRoZSBkZW5zaXR5IG9mIGxpbmtzIGluc2lkZSBjb21tdW5pdGllcyBpbiByZWxhdGlvbiB0byB0aGUgZGVuc2l0eSBvZiBsaW5rcyBiZXR3ZWVuIGNvbW11bml0aWVzIChGb3J0dW5hdG8sIDIwMTApLiBOZXR3b3JrcyB3aXRoIGhpZ2hlciBRIGFyZSBzYWlkIHRvIHNob3cgc3Ryb25nIGV2aWRlbmNlIG9mIGNvbW11bml0eSBzdHJ1Y3R1cmUuIAoKIVtdKGltZy9rYXJhdGUtY29tbXVuaXRpZXMucG5nKQoKKkNvbW11bml0aWVzIGFyZSBkZXBpY3RlZCBpbiBkaWZmZXJlbnQgY29sb3JzIGZyb20gYW5vdGhlciBmYW1vdXMgbmV0d29yazogWmFjaGFyeSdzIEthcmF0ZSBDbHViIE5ldHdvcmsqCgoqKkhvdyBkbyBuZXR3b3JrIHNjaWVudGlzdHMgImZpbmQiIGNvbW11bml0aWVzIGluIG5ldHdvcmtzPyoqIAoKTWFueSBjb21tdW5pdHkgZGV0ZWN0aW9uIG1ldGhvZHMgaGF2ZSBiZWVuIGRldmVsb3BlZCBieSBuZXR3b3JrIHNjaWVudGlzdHMgdG8gZGV0ZWN0IGNvbW11bml0aWVzIGluIG5ldHdvcmtzLiBJdCBpcyBzb3J0IG9mIGxpa2UgYSAiY2x1c3RlcmluZyBhbmFseXNpcyIgZm9yIG5ldHdvcmsgc2NpZW50aXN0cy4gSGVyZSB3ZSB3aWxsIGdvIHRocm91Z2ggZm91ciBleGFtcGxlcyB0aGF0IHJlZmxlY3QgYnJvYWQgY2xhc3NlcyBvZiBjb21tdW5pdHkgZGV0ZWN0aW9uIHRlY2huaXF1ZXMuIEVhY2ggZGlmZmVycyBpbiB0aGVpciBpbXBsZW1lbnRhdGlvbiwgYW5kIHJlZmxlY3RzIHRoZSBjcmVhdG9yJ3MgaW1wbGljaXQgZGVmaW5pdGlvbiBvZiB3aGF0IGlzIGEgY29tbXVuaXR5LiAKCk5vdGU6IEluIHRoaXMgdHV0b3JpYWwgdGhlIGNvbW11bml0eSBkZXRlY3Rpb24gaXMgb25seSBpbXBsZW1lbnRlZCBvbiBgZ19wbmV0X2Fkam1hdGAsIGFuIHVuZGlyZWN0ZWQgYW5kIHVud2VpZ2h0ZWQgbmV0d29yay4gVGhlIHVzdWFsIGFyZ3VtZW50cyBmb3IgYHdlaWdodHNgIGFuZCBgZGlyZWN0ZWRgIGFyZSBhdmFpbGFibGUgaW4gdGhlIGNvbW11bml0eSBkZXRlY3Rpb24gZnVuY3Rpb24gaWYgeW91IHdpc2ggdG8gdG9nZ2xlIHRoZXNlIG9uIGZvciB3ZWlnaHRlZCBhbmQgZGlyZWN0ZWQgbmV0d29ya3MuIAoKIyMjIEVkZ2UgYmV0d2Vlbm5lc3MgKCJkaXZpc2l2ZSBtZXRob2QiKQoKVGhlIGNvcmUgaWRlYSBiZWhpbmQgdGhpcyB0ZWNobmlxdWUgaXMgdGhhdCBlZGdlcyBjb25uZWN0aW5nIHNlcGFyYXRlIGNvbW11bml0aWVzIHRlbmQgdG8gaGF2ZSBoaWdoICoqZWRnZSBiZXR3ZWVubmVzcyoqIGFzIGFsbCB0aGUgc2hvcnRlc3QgcGF0aHMgZnJvbSBvbmUgY29tbXVuaXR5IHRvIGFub3RoZXIgbXVzdCB0cmF2ZXJzZSB0aHJvdWdoIHRoZW0uIAoKVGhlIGFsZ29yaXRobSB3b3JrcyBieSBjYWxjdWxhdGluZyB0aGUgZWRnZSBiZXR3ZWVubmVzcyBvZiBhbGwgZWRnZXMgdGhlIGdyYXBoLCAqcmVtb3ZpbmcqIHRoZSBlZGdlIHdpdGggdGhlIGhpZ2hlc3QgZWRnZSBiZXR3ZWVubmVzcyBzY29yZSwgdGhlbiByZWNhbGN1bGF0aW5nIGVkZ2UgYmV0d2Vlbm5lc3Mgb2YgcmVtYWluaW5nIGVkZ2VzIGFuZCBhZ2FpbiByZW1vdmluZyB0aGUgb25lIHdpdGggdGhlIGhpZ2hlc3Qgc2NvcmUuIFRoaXMgcmVwZWF0cyB1bnRpbCBtb2R1bGFyaXR5IGNhbm5vdCBiZSBpbXByb3ZlZCBmdXJ0aGVyLiAgCgpgYGB7cn0KIyBydW4gdGhlIGNvbW11bml0eSBkZXRlY3Rpb24gYWxnb3JpdGhtIApyZXN1bHRzX2VkZ2UgPC0gY2x1c3Rlcl9lZGdlX2JldHdlZW5uZXNzKGdyYXBoID0gZ19wbmV0X2Fkam1hdCkKCiMgb3ZlcmFsbCByZXN1bHRzIAptb2R1bGFyaXR5KHJlc3VsdHNfZWRnZSkKc2l6ZXMocmVzdWx0c19lZGdlKQoKIyBzcGVjaWZpYyBjb21tdW5pdHkgbWVtYmVyc2hpcCBmb3IgZWFjaCBub2RlIApjYmluZCgKICByZXN1bHRzX2VkZ2UkbmFtZXMsCiAgcmVzdWx0c19lZGdlJG1lbWJlcnNoaXAKKSB8PiBoZWFkKDUpCmBgYAoKU2F2aW5nIHRoZSBjb21tdW5pdHkgZGV0ZWN0aW9uIHJlc3VsdHMgYXMgYSBgY29tbXVuaXRpZXNgIG9iamVjdCBlbmFibGVzIHRoZSB1c2Ugb2Ygc3BlY2lhbCBmdW5jdGlvbnMgbGlrZSBgbW9kdWxhcml0eSgpYCBhbmQgYHNpemVzKClgIHRvIG9idGFpbiB0aGUgbW9kdWxhcml0eSBvZiB0aGUgbmV0d29yayBhbmQgaXRzIGNvbW11bml0eSBzaXplcy4gSSBoYXZlIGFsc28gaW5jbHVkZWQgY29kZSB0aGF0IHNob3dzIGhvdyB0byBleHRyYWN0IHRoZSBjb21tdW5pdHkgbWVtYmVyc2hpcHMgb2YgYWxsIG5vZGVzIGluIHRoZSBuZXR3b3JrIGZvciBmdXJ0aGVyIGFuYWx5c2lzLiBUaGlzIGFwcGxpZXMgdG8gdGhlIG90aGVyIGNvbW11bml0eSBkZXRlY3Rpb24gYWxnb3JpdGhtcyBhcyB3ZWxsLiAKCiMjIyBMb3V2YWluIG1ldGhvZCAoImdyZWVkeSwgbWF4aW1pemF0aW9uIG1ldGhvZCIpCgpUaGUgY29yZSBpZGVhIGJlaGluZCB0aGlzIG1ldGhvZCBpcyB0aGF0IGNvbW11bml0aWVzIGFyZSBlc3NlbnRpYWxseSDigJxtZXJnZXJz4oCdIG9mIHNtYWxsIGNvbW11bml0aWVzIChCbG9uZGVsIGV0IGFsLiwgMjAwOCksIHJlZmxlY3RpbmcgdGhlIHNlbGYtc2ltaWxhciBuYXR1cmUgb2YgY29tcGxleCBuZXR3b3Jrcy4KCjEuIEVhY2ggbm9kZSBpcyBhc3NpZ25lZCB0byBvbmUgY29tbXVuaXR5IHN1Y2ggdGhhdCB0aGVyZSBhcmUgYXMgbWFueSBjb21tdW5pdGllcyBhcyB0aGVyZSBhcmUgbm9kZXMuIFRoZW4gcmVtb3ZlIG5vZGUgKmkqIGZyb20gaXRzIGNvbW11bml0eSBhbmQgcGxhY2luZyBpdCBpbiB0aGUgY29tbXVuaXR5IG9mIHRoZSBuZWlnaGJvciB3aGljaCB5aWVsZHMgdGhlIGdyZWF0ZXN0IGdhaW4gaW4gbW9kdWxhcml0eS4gCiAgLSByZXBlYXQgZm9yIGFsbCBub2RlcyBpbiB0aGUgbmV0d29yawoKMi4gQSBuZXcgbmV0d29yayBpcyBidWlsdCB3aGVyZSBub2RlcyBhcmUgdGhlICpjb21tdW5pdGllcyBmb3VuZCBpbiB0aGUgcHJldmlvdXMgcGhhc2UqLiBSZXBlYXQgU3RlcCAxLiAKICAtIHJlcGVhdCBTdGVwIDEgYW5kIDIgdW50aWwgaXQgaXMgbm90IHBvc3NpYmxlIHRvIGZ1cnRoZXIgaW5jcmVhc2UgdGhlIHZhbHVlIG9mIFEKCmBgYHtyfQojIHJ1biB0aGUgY29tbXVuaXR5IGRldGVjdGlvbiBhbGdvcml0aG0gCnJlc3VsdHNfbG91dmFpbiA8LSBjbHVzdGVyX2xvdXZhaW4oZ3JhcGggPSBnX3BuZXRfYWRqbWF0KQoKIyBvdmVyYWxsIHJlc3VsdHMgCm1vZHVsYXJpdHkocmVzdWx0c19sb3V2YWluKQpzaXplcyhyZXN1bHRzX2xvdXZhaW4pCgojIHNwZWNpZmljIGNvbW11bml0eSBtZW1iZXJzaGlwIGZvciBlYWNoIG5vZGUgCmNiaW5kKAogIHJlc3VsdHNfbG91dmFpbiRuYW1lcywKICByZXN1bHRzX2xvdXZhaW4kbWVtYmVyc2hpcAopIHw+IGhlYWQoNSkKYGBgCgojIyMgUmFuZG9tIHdhbGtlciAoImR5bmFtaWMgbWV0aG9kIikKClRoZSBjb3JlIGlkZWEgYmVoaW5kIHRoaXMgbWV0aG9kIGlzIHRoYXQgaWYgdGhlcmUgYXJlIGNvbW11bml0aWVzIGluIHRoZSBuZXR3b3JrLCBhIHJhbmRvbSB3YWxrZXIgd2lsbCB0ZW5kIHRvIHNwZW5kIG1vcmUgdGltZSBpbnNpZGUgdGhlIGNvbW11bml0eSB0aGFuIG91dHNpZGUuIAoKVGhlIFdhbGt0cmFwIGFsZ29yaXRobSBncm91cHMgbm9kZXMgdG9nZXRoZXIgYmFzZWQgb24gdGhlIHNpbWlsYXJpdGllcyBvZiB0aGUgcGF0aHMgdGFrZW4gYnkgdGhlIHJhbmRvbSB3YWxrZXIgc3RhcnRpbmcgZnJvbSB0aGF0IG5vZGUuIFRoZSBpZGVhIGlzIHRvIG1lcmdlIHNldHMgb2YgdmVydGljZXMgdGhhdCBoYXZlIGxvdyAiZGlzdGFuY2UiIGZyb20gZWFjaCBvdGhlci4gCgpgYGB7cn0KIyBydW4gdGhlIGNvbW11bml0eSBkZXRlY3Rpb24gYWxnb3JpdGhtIApyZXN1bHRzX3dhbGt0cmFwIDwtIGNsdXN0ZXJfd2Fsa3RyYXAoZ3JhcGggPSBnX3BuZXRfYWRqbWF0KQoKIyBvdmVyYWxsIHJlc3VsdHMgCm1vZHVsYXJpdHkocmVzdWx0c193YWxrdHJhcCkKc2l6ZXMocmVzdWx0c193YWxrdHJhcCkKCiMgc3BlY2lmaWMgY29tbXVuaXR5IG1lbWJlcnNoaXAgZm9yIGVhY2ggbm9kZSAKY2JpbmQoCiAgcmVzdWx0c193YWxrdHJhcCRuYW1lcywKICByZXN1bHRzX3dhbGt0cmFwJG1lbWJlcnNoaXAKKSB8PiBoZWFkKDUpCmBgYAoKIyMjIEluZm9tYXAgKCJpbmZvcm1hdGlvbi10aGVvcmV0aWMgbWV0aG9kIikKClRoZSBjb3JlIGlkZWEgYmVoaW5kIHRoaXMgYWxnb3JpdGhtIGlzIHRvIGxldmVyYWdlIG9uIGluZm9ybWF0aW9uLXRoZW9yZXRpYyBtZXRob2RzIHRvICJkZXNjcmliZSIgdGhlIGluZm9ybWF0aW9uIGZsb3cgb2YgdGhlIGVudGlyZSBzeXN0ZW0gKGJhc2VkIG9uIHJhbmRvbSB3YWxrcykuICAKClRoZSBJbmZvbWFwIGFsZ29yaXRobSBhdHRlbXB0cyB0byBkZXNjcmliZSB0aGUgcmFuZG9tIHdhbGtlcidzIHRyYWplY3RvcnkgdXNpbmcgdGhlIGZld2VzdCBudW1iZXIgb2YgImJpdHMiIG9mIGluZm9ybWF0aW9uLiBDb21tdW5pdGllcyBhcmUgZ3JvdXBzIG9mIG5vZGVzIHRoYXQgcmVjZWl2ZSBuZXcgIm5hbWVzIiBkdXJpbmcgdGhlIGNvbXByZXNzaW9uLiAKCmBgYHtyfQojIHJ1biB0aGUgY29tbXVuaXR5IGRldGVjdGlvbiBhbGdvcml0aG0gCnJlc3VsdHNfaW5mb21hcCA8LSBjbHVzdGVyX2luZm9tYXAoZ3JhcGggPSBnX3BuZXRfYWRqbWF0KQoKIyBvdmVyYWxsIHJlc3VsdHMgCm1vZHVsYXJpdHkocmVzdWx0c19pbmZvbWFwKQpzaXplcyhyZXN1bHRzX2luZm9tYXApCgojIHNwZWNpZmljIGNvbW11bml0eSBtZW1iZXJzaGlwIGZvciBlYWNoIG5vZGUgCmNiaW5kKAogIHJlc3VsdHNfaW5mb21hcCRuYW1lcywKICByZXN1bHRzX2luZm9tYXAkbWVtYmVyc2hpcAopIHw+IGhlYWQoNSkKYGBgCgojIyMgQ29tcGFyaXNvbiBvZiBtZXRob2RzIAoKRm9ydHVuYXRvICgyMDEwKSBzdW1tYXJpemVkIHBhcGVycyB0aGF0IGNvbmR1Y3RlZCBhIGNvbXByZWhlbnNpdmUgY29tcGFyaXNvbiBvZiBjb21tdW5pdHkgZGV0ZWN0aW9uIHRlY2huaXF1ZXMuIAoKR2VuZXJhbGx5LCBSb3N2YWxsICYgQmVyZ3N0b3JtJ3MgSW5mb21hcCBhbmQgQmxvbmRlbCBldCBhbC4ncyBncmVlZHkgbW9kdWxhcml0eSBtYXhpbWl6YXRpb24gbWV0aG9kIHBlcmZvcm1lZCB0aGUgYmVzdC4gQm90aCBhbHNvIHdlcmUgcmVsYXRpdmVseSBmYXN0IGFsZ29yaXRobXMuIAoKVGhlIGNvZGUgYmVsb3cgaWxsdXN0cmF0ZXMgdGhlIHNpbWlsYXJpdGllcyBhbmQgZGlmZmVyZW5jZXMgaW4gdGhlIHJlc3VsdHMgb2YgdGhlIHZhcmlvdXMgY29tbXVuaXR5IGRldGVjdGlvbiBtZXRob2RzLiAKCmBgYHtyfQojIGNvbXBhcmluZyB0aGUgUXMKCnJiaW5kKAogIGMoJ2VkZ2VfYmV0d2Vlbm5lc3MnLCAnTG91dmFpbicsICdXYWxrdHJhcCcsICdJbmZvbWFwJyksCiAgYyhtb2R1bGFyaXR5KHJlc3VsdHNfZWRnZSksIG1vZHVsYXJpdHkocmVzdWx0c19sb3V2YWluKSwgbW9kdWxhcml0eShyZXN1bHRzX3dhbGt0cmFwKSwgbW9kdWxhcml0eShyZXN1bHRzX2luZm9tYXApKSB8PiByb3VuZCgzKQopCgojIGNvbXBhcmluZyBjb21tdW5pdHkgbWVtYmVyc2hpcCAKCnBhcihtYXI9YygwLDAsMCwwKSsuNiwgbWZyb3cgPSBjKDIsMikpICMgcmVkdWNlIG1hcmdpbnMgYW5kIHBsb3QgYm90aCBuZXR3b3JrcyB0b2dldGhlcgoKc2V0LnNlZWQoMSkKZml4ZWRfbCA8LSBsYXlvdXRfd2l0aF9mcihnX3BuZXRfYWRqbWF0KSAjIHRvIGZpeCBub2RlIGxheW91dCBhY3Jvc3MgcGxvdHMgCgpwbG90KHJlc3VsdHNfZWRnZSwgZ19wbmV0X2Fkam1hdCwgbGF5b3V0ID0gZml4ZWRfbCwgbWFpbiA9ICdlZGdlIGJldHdlZW5uZXNzJykKcGxvdChyZXN1bHRzX2xvdXZhaW4sIGdfcG5ldF9hZGptYXQsIGxheW91dCA9IGZpeGVkX2wsIG1haW4gPSAnTG91dmFpbicpCnBsb3QocmVzdWx0c193YWxrdHJhcCwgZ19wbmV0X2Fkam1hdCwgbGF5b3V0ID0gZml4ZWRfbCwgbWFpbiA9ICdXYWxrdHJhcCcpCnBsb3QocmVzdWx0c19pbmZvbWFwLCBnX3BuZXRfYWRqbWF0LCBsYXlvdXQgPSBmaXhlZF9sLCBtYWluID0gJ0luZm9tYXAnKQpgYGAKCiMjIE1hY3JvLWxldmVsIChuZXR3b3JrLWxldmVsKQoKSW4gdGhpcyBzZWN0aW9uLCB3ZSB3aWxsIHJldmlldyBuZXR3b3JrIHNjaWVuY2UgbWVhc3VyZXMgdGhhdCBkZXNjcmliZSB0aGUgb3ZlcmFsbCBvciBnbG9iYWwgc3RydWN0dXJlIG9mIHRoZSBlbnRpcmUgbmV0d29yay4gWW91IGNhbiB0aGluayBvZiB0aGVzZSBtZWFzdXJlcyBhcyBwcm92aWRpbmcgYSAiYmlyZCdzIGV5ZSB2aWV3IiBvZiB5b3VyIG5ldHdvcmssIGFuZCB0aGV5IGFyZSB1c2VmdWwgZm9yIGNvbXBhcmluZyBkaWZmZXJlbnQgbmV0d29yayByZXByZXNlbnRhdGlvbnMuIAoKIyMjIEF2ZXJhZ2UgU2hvcnRlc3QgUGF0aCBMZW5ndGggCgoqKkF2ZXJhZ2Ugc2hvcnRlc3QgcGF0aCBsZW5ndGgqKiAoQVNQTCkgcmVmZXJzIHRvIHRoZSBtZWFuIG9mIHRoZSBzaG9ydGVzdCBwb3NzaWJsZSBwYXRoIGJldHdlZW4gYWxsIHBvc3NpYmxlIHBhaXJzIG9mIG5vZGVzIGluIHRoZSBuZXR3b3JrLiAoVGhpcyBsb29zZWx5IGNvcnJlc3BvbmRzIHRvIHRoZSBpZGVhIG9mICJzaXggZGVncmVlcyBvZiBzZXBhcmF0aW9uIiBpbiBzb2NpYWwgbmV0d29ya3MuKSAgCgohW10oaHR0cHM6Ly9leHRlcm5hbC1jb250ZW50LmR1Y2tkdWNrZ28uY29tL2l1Lz91PWh0dHBzJTNBJTJGJTJGdHNlMi5tbS5iaW5nLm5ldCUyRnRoJTNGaWQlM0RPSVAuN001cG1HNHc1Tm5wNjEwaTFqN3JMUUhhRnYlMjZwaWQlM0RBcGkmZj0xKQoKKkV4YW1wbGUgZGVwaWN0aW5nIHRoZSBzaG9ydGVzdCBwYXRoIGJldHdlZW4gbm9kZXMgMjUgYW5kIDE2LioKCmBgYHtyfQojIHVuZGlyZWN0ZWQsIHVud2VpZ2h0ZWQgbmV0d29yayAKYXZlcmFnZS5wYXRoLmxlbmd0aChncmFwaCA9IGdfcG5ldF9hZGptYXQsIHdlaWdodHMgPSBOQSwgZGlyZWN0ZWQgPSBGKQphdmVyYWdlLnBhdGgubGVuZ3RoKGdyYXBoID0gZ19wbmV0X2Fkam1hdCkgIyBkZWZhdWx0IHZhbHVlcyBmb3Igd2VpZ2h0cyBhbmQgZGlyZWN0ZWQgZ2l2ZSB0aGUgc2FtZSB2YWx1ZXMgc2luY2UgdGhpcyBpcyBhbiB1bmRpcmVjdGVkLCB1bndlaWdodGVkIG5ldHdvcmsgCgojIGFuIGFsdGVybmF0aXZlIGZ1bmN0aW9uIC0gYm90aCBnaXZlIHRoZSBzYW1lIHJlc3VsdCAKbWVhbl9kaXN0YW5jZShncmFwaCA9IGdfcG5ldF9hZGptYXQpCgojIGRpcmVjdGVkLCB3ZWlnaHRlZCBuZXR3b3JrIAptZWFuX2Rpc3RhbmNlKGdyYXBoID0gZ19zbmV0X2Fkam1hdCwgd2VpZ2h0cyA9IE5VTEwsIGRpcmVjdGVkID0gVCkKbWVhbl9kaXN0YW5jZShncmFwaCA9IGdfc25ldF9hZGptYXQsIHdlaWdodHMgPSBOVUxMLCBkaXJlY3RlZCA9IEYpICMgaWdub3JlIGRpcmVjdGlvbiAKbWVhbl9kaXN0YW5jZShncmFwaCA9IGdfc25ldF9hZGptYXQsIHdlaWdodHMgPSBOQSwgZGlyZWN0ZWQgPSBUKSAjIGlnbm9yZSB3ZWlnaHRzIApgYGAKCiMjIyBHbG9iYWwgQ2x1c3RlcmluZyBDb2VmZmljaWVudCAgCgoqKkdsb2JhbCBjbHVzdGVyaW5nIGNvZWZmaWNpZW50KiogcmVmZXJzIHRvIHRoZSBudW1iZXIgb2YgY2xvc2VkIHRyaWFuZ2xlcyBpbiB0aGUgbmV0d29yayByZWxhdGl2ZSB0byB0aGUgbnVtYmVyIG9mIHBvc3NpYmxlIHRyaWFuZ2xlcy4gSXQgaXMgYSBtZWFzdXJlIG9mIG92ZXJhbGwgbGV2ZWwgb2YgKmxvY2FsKiBjb25uZWN0aXZpdHkgYW1vbmcgbm9kZXMgaW4gdGhlIG5ldHdvcmsuIAoKQSBzaW1wbGUgd2F5IG9mIHRoaW5raW5nIGFib3V0IHRoaXMgY29uY2VwdCBpcyB0aGF0IGl0IGlzIG1lYXN1cmluZyB0aGUgcHJvYmFiaWxpdHkgdGhhdCBlYWNoIHBhaXIgb2YgImZyaWVuZHMiIG9mIGEgZ2l2ZW4gbm9kZSBhcmUgYWxzbyBmcmllbmRzIHdpdGggZWFjaCBvdGhlci4KCmBgYHtyfQp0cmFuc2l0aXZpdHkoZ3JhcGggPSBnX3BuZXRfYWRqbWF0LCB0eXBlID0gJ2dsb2JhbCcpCnRyYW5zaXRpdml0eShncmFwaCA9IGdfc25ldF9hZGptYXQsIHR5cGUgPSAnZ2xvYmFsJykKYGBgCgojIyMgU21hbGwgV29ybGQgSW5kZXggCgpUaGUgdGVybSAic21hbGwgd29ybGQiIGhhcyBhIHNwZWNpZmljIG1lYW5pbmcgaW4gbmV0d29yayBzY2llbmNlIGFzIGNvbXBhcmVkIHRvIHRoZSBsYXlwZXJzb24ncy4gQSBuZXR3b3JrIGlzIGNvbnNpZGVyZWQgdG8gaGF2ZSBzbWFsbCB3b3JsZCBjaGFyYWN0ZXJpc3RpY3MgaWYgKGkpIGl0cyBBU1BMIGlzICpzaG9ydGVyKiB0aGFuIHRoYXQgb2YgYSByYW5kb21seSBnZW5lcmF0ZWQgbmV0d29yayB3aXRoIHRoZSBzYW1lIG51bWJlciBvZiBub2RlcyBhbmQgZWRnZXMsIGFuZCAoaWkpIGl0cyBnbG9iYWwgQyBpcyAqbGFyZ2VyKiB0aGFuIHRoYXQgb2YgYSByYW5kb21seSBnZW5lcmF0ZWQgbmV0d29yayB3aXRoIHRoZSBzYW1lIG51bWJlciBvZiBub2RlcyBhbmQgZWRnZXMuIFRoZXJlIGFyZSB2YXJpb3VzIHdheXMgdG8gY29tcHV0ZSBhIHZhbHVlIHRoYXQgcXVhbnRpZmllcyB0aGUgInNtYWxsIHdvcmxkbmVzcyIgb2YgYSBuZXR3b3JrLCBhbHRob3VnaCB3ZSBkbyBub3QgY292ZXIgdGhlbSBoZXJlIChzZWUgSHVtcGhyaWVzIGFuZCBHdXJuZXksIDIwMDgsIGZvciBhbiBleGFtcGxlLCBhbmQgTmVhbCwgMjAxNywgZm9yIGEgY29tcGFyaXNvbiBvZiBkaWZmZXJlbnQgbWV0aG9kcykuCgpUaGUgbWFpbiB0YWtlIGhvbWUgbWVzc2FnZSBpcyB0aGF0IGEgc21hbGwgd29ybGQgbmV0d29yayBoYXMgaGlnaCBsZXZlbHMgb2YgbG9jYWwgY2x1c3RlcmluZyAobm9kZXMgd2hvc2UgbmVpZ2hib3JzIGFyZSBhbHNvIG5laWdoYm9ycyBvZiBlYWNoIG90aGVyKSwgYnV0IHRoZXJlIGFsc28gZXhpc3RzIGEgbnVtYmVyIG9mIHNob3J0Y3V0cyB0aGF0IGRyYXN0aWNhbGx5IHJlZHVjZXMgdGhlIG92ZXJhbGwgZGlzdGFuY2VzL3BhdGggbGVuZ3RocyBiZXR3ZWVuIG5vZGVzLiBTZWUgYmVsb3cgZm9yIGFuIGlsbHVzdHJhdGlvbiBvZiB0aGlzIGlkZWEuIAoKIVtdKGh0dHBzOi8vb25saW5lbGlicmFyeS53aWxleS5jb20vY21zL2Fzc2V0LzNlZjVkZjdlLTJmMzYtNGZkZS05YjY0LTc0ZTFlMzU1NGQxZC9uZmcwMDguZ2lmKQoKIyMjIE5ldHdvcmsgRGVuc2l0eQoKKipOZXR3b3JrIGRlbnNpdHkqKiByZWZlcnMgdG8gdGhlIHJhdGlvIG9mIHRoZSBudW1iZXIgb2YgKGV4aXN0aW5nKSBlZGdlcyBhbmQgdGhlIG51bWJlciBvZiBwb3NzaWJsZSBlZGdlcyBhbW9uZyBub2RlcyBpbiB0aGUgbmV0d29yay4gCgohW10oaHR0cHM6Ly9jZG4uZnMuZ3VpZGVzLmNvL1BEbjBJbVRmU2I2UXdnSXZkb1E4KQoKKlNpbXBsZSBleGFtcGxlIG9mIG5ldHdvcmtzIHdpdGggbG93ZXIgYW5kIGhpZ2hlciBuZXR3b3JrIGRlbnNpdGllcy4qCgpgYGB7cn0KZ3JhcGguZGVuc2l0eShncmFwaCA9IGdfcG5ldF9hZGptYXQpCmdyYXBoLmRlbnNpdHkoZ3JhcGggPSBnX3NuZXRfYWRqbWF0KQpgYGAKCiMjIyBOZXR3b3JrIERpYW1ldGVyIAoKKipOZXR3b3JrIGRpYW1ldGVyKiogcmVmZXJzIHRvIGxlbmd0aCBvZiB0aGUgbG9uZ2VzdCBzaG9ydGVzdCBwYXRoIGJldHdlZW4gbm9kZXMgaW4gdGhlIG5ldHdvcmsuIEluc3RlYWQgb2YgZ2V0dGluZyB0aGUgbWVhbiBvZiBhbGwgdGhlIHNob3J0ZXN0IHBhdGhzIGFzIHlvdSBkaWQgaW4gQVNQTCwgd2hhdCBpcyB0aGUgKm1heGltdW0qIGxlbmd0aCBvZiB0aG9zZSBzaG9ydCBwYXRocz8gCgohW10oaHR0cHM6Ly93d3cucmVzZWFyY2hnYXRlLm5ldC9wcm9maWxlL0dpb3Zhbm5pLVNjYXJkb25pL3B1YmxpY2F0aW9uLzIyMTkyNjYyMy9maWd1cmUvZmlnMS9BUzozMDUyOTY4NzEzMTM0MDhAMTQ0OTc5OTg1NDY0Mi9hLUEtbmV0d29yay13aGVyZS1oaWdoLWRpYW1ldGVyLWlzLWR1ZS10by1hLWxvdy1udW1iZXItb2Ytbm9kZXMtYi1BLW5ldHdvcmstd2l0aC1sb3cucG5nKQoKKlNpbXBsZSBleGFtcGxlIG9mIG5ldHdvcmtzIHdpdGggaGlnaGVyIGFuZCBsb3dlciBuZXR3b3JrIGRpYW1ldGVycyoKCmBgYHtyfQojIHVuZGlyZWN0ZWQsIHVud2VpZ2h0ZWQgZ3JhcGggCmRpYW1ldGVyKGdyYXBoID0gZ19wbmV0X2Fkam1hdCwgZGlyZWN0ZWQgPSBGLCB3ZWlnaHRzID0gTkEpCmRpYW1ldGVyKGdyYXBoID0gZ19wbmV0X2Fkam1hdCkKCiMgZGlyZWN0ZWQsIHdlaWdodGVkIGdyYXBoIApkaWFtZXRlcihncmFwaCA9IGdfc25ldF9hZGptYXQsIGRpcmVjdGVkID0gVCwgd2VpZ2h0cyA9IE5VTEwpCmRpYW1ldGVyKGdyYXBoID0gZ19zbmV0X2Fkam1hdCwgZGlyZWN0ZWQgPSBGLCB3ZWlnaHRzID0gTlVMTCkgIyBpZ25vcmUgZGlyZWN0aW9uIApkaWFtZXRlcihncmFwaCA9IGdfc25ldF9hZGptYXQsIGRpcmVjdGVkID0gVCwgd2VpZ2h0cyA9IE5BKSAjIGlnbm9yZSB3ZWlnaHRzIApgYGAKCiMgQXBwZW5kaXg6IE5ldHdvcmsgVmlzdWFsaXphdGlvbiAKClRoZSBwdXJwb3NlIG9mIHRoaXMgc2VjdGlvbiBpcyB0byBwcm92aWRlIGEgZ2VudGxlIGludHJvZHVjdGlvbiB0byBuZXR3b3JrIHZpc3VhbGl6YXRpb24gaW4gYGlncmFwaGAuIEdlbmVyYWxseSwgaXQgaXMgYWR2aXNhYmxlIHRvIG9ubHkgdmlzdWFsaXplIHNtYWxsIG5ldHdvcmtzIG9yIGEgc3Vic2V0IG9mIGEgbGFyZ2VyIG5ldHdvcms7IHRoaXMgaXMgYmVjYXVzZSBpdCBxdWlja2x5IGJlY29tZXMgdG9vIGNoYWxsZW5naW5nIHRvIGRldmVsb3AgYSBtZWFuaW5nZnVsIHZpc3VhbCByZXByZXNlbnRhdGlvbiBvZiBhIGxhcmdlIG5ldHdvcmsgd2l0aCBtYW55IG5vZGVzIGFuZCBlZGdlcy4gCgpGb3IgdGhlIHB1cnBvc2VzIG9mIHRoZSB0dXRvcmlhbCB3ZSB3aWxsIHdvcmsgd2l0aCBhIHJhbmRvbWx5IGdlbmVyYXRlZCBuZXR3b3JrIGBnYC4gVGhlIGRlZmF1bHQgcGxvdCBkb2VzIG5vdCBsb29rIG5pY2UuLi4KCmBgYHtyIGRlZmF1bHQtcGxvdH0KcGFyKG1hcj1jKDAsMCwwLDApKy4xKSAjIHJlZHVjZSBtYXJnaW5zCgpzZXQuc2VlZCg1KQpnIDwtIHNhbXBsZV9nbnAobiA9IDIwLCBwID0gMC4yMCkgIyAyMCBub2RlcyB3aXRoIGVkZ2UgcHJvYmFiaWxpdHkgb2YgMC4yCnBsb3QoZykKYGBgCgojIyBOb2RlIFBhcmFtZXRlcnMgCgpUaGlzIGNvZGUgY2h1bmsgaWxsdXN0cmF0ZXMgYSBmZXcgb2YgdGhlIG1vc3QgY29tbW9ubHkgdXNlZCBub2RlL3ZlcnRleCBwYXJhbWV0ZXJzIGluIHZpc3VhbGl6YXRpb24uIAoKYGBge3Igbm9kZS1hcmdzfQpwYXIobWFyPWMoMCwwLDAsMCkrLjEpICMgcmVkdWNlIG1hcmdpbnMKCnBsb3QoZywKICAgICB2ZXJ0ZXguY29sb3IgPSAnZGFya29yY2hpZDEnLCAjIGNoYW5nZSBjb2xvciBvZiBub2RlcyAKICAgICB2ZXJ0ZXguZnJhbWUuY29sb3IgPSAnbGlnaHRncmV5JywgIyBjaGFuZ2UgdGhlIG91dGxpbmUgY29sb3Igb2Ygbm9kZXMgCiAgICAgdmVydGV4LmxhYmVsLmRpc3QgPSAxLjcsICMgYWRqdXN0IGRpc3RhbmNlIG9mIG5vZGUgbGFiZWwgZnJvbSBub2RlIAogICAgIHZlcnRleC5sYWJlbC5mYW1pbHkgPSAnc2FucycsICMgY2hhbmdlIGZvbnQgCiAgICAgdmVydGV4LnNpemUgPSBkZWdyZWUoZykgIyBzaXplIG9mIG5vZGUgY29ycmVzcG9uZHMgdG8gaXRzIGRlZ3JlZSAKICAgICApCmBgYAoKIyMgRWRnZSBQYXJhbWV0ZXJzIAoKVG8gaWxsdXN0cmF0ZSB0aGUgZWRnZSBwYXJhbWV0ZXJzLCBhIHdlaWdodGVkIGFuZCBkaXJlY3RlZCBuZXR3b3JrIGBnd2AgaXMgY3JlYXRlZC4gVGhlIGNvZGUgY2h1bmsgYmVsb3cgaWxsdXN0cmF0ZXMgYSBmZXcgb2YgdGhlIG1vc3QgY29tbW9ubHkgdXNlZCBlZGdlIHBhcmFtZXRlcnMgaW4gdmlzdWFsaXphdGlvbi4gCgpgYGB7ciBlZGdlLWFyZ3N9CnNldC5zZWVkKDkpCmd3IDwtIHNhbXBsZV9nbnAobiA9IDIwLCBwID0gMC4yMCwgZGlyZWN0ZWQgPSBUKSAjIDIwIG5vZGVzIHdpdGggZWRnZSBwcm9iYWJpbGl0eSBvZiAwLjIsIGVkZ2VzIGFyZSBkaXJlY3RlZApFKGd3KSR3ZWlnaHQgPC0gc2FtcGxlKDE6NSwgc2l6ZSA9IGdzaXplKGd3KSwgcmVwbGFjZSA9IFQpICMgcmFuZG9tbHkgYWRkIGVkZ2Ugd2VpZ2h0cyBvZiAxIHRvIDUgCnN1bW1hcnkoZ3cpICMgdGhlICdEVycgaW5kaWNhdGVzIGEgZGlyZWN0ZWQgYW5kIHdlaWdodGVkIG5ldHdvcmsgCgpwYXIobWFyPWMoMCwwLDAsMCkrLjEpICMgcmVkdWNlIG1hcmdpbnMKCnBsb3QoZ3csCiAgICAgZWRnZS5jb2xvciA9ICdkYXJrb2xpdmVncmVlbicsICMgY29sb3Igb2YgZWRnZXMgCiAgICAgZWRnZS53aWR0aCA9IEUoZ3cpJHdlaWdodCwgIyB0aGUgd2lkdGggb2YgZWRnZXMgY29ycmVzcG9uZHMgdG8gdGhlIGVkZ2Ugd2VpZ2h0IAogICAgIGVkZ2UuY3VydmVkID0gMC41LCAjIGFkZCBjdXJ2YXR1cmUgdG8gZWRnZXMgCiAgICAgZWRnZS5hcnJvdy53aWR0aCA9IDAuNSwgIyBhZGp1c3QgYXJyb3cgd2lkdGgKICAgICBlZGdlLmFycm93LnNpemUgPSAwLjggIyBhZGp1c3QgYXJyb3cgc2l6ZSAKICAgICApCmBgYAoKIyMgTmV0d29yayBMYXlvdXRzIAoKWW91IGNhbiBhbHNvIGFkanVzdCB0aGUgb3ZlcmFsbCBsYXlvdXQgb2YgdGhlIG5ldHdvcmsuIFRoZXNlIGxheW91dHMgYXJlIGRpZmZlcmVudCBuZXR3b3JrIHZpc3VhbGl6YXRpb24gYXBwcm9hY2hlcyB0aGF0IHVzZSB2YXJpb3VzIGFsZ29yaXRobXMgdG8gZGVjaWRlIGhvdyBub2RlcyBzaG91bGQgYmUgYmVzdCBwb3NpdGlvbmVkIG9uIGEgMkQgcGxhbmUsIHdoaWxlIGNvbnNpZGVyaW5nIHRoZSBuYXR1cmUgb2YgdGhlaXIgZWRnZSBjb25uZWN0aXZpdHkuIFRoZXJlIGFyZSBtYW55IGRpZmZlcmVudCBsYXlvdXRzIGF2YWlsYWJsZSAtIHlvdSBjYW4gZWl0aGVyIGNoZWNrIG91dCB0aGUgYGlncmFwaGAgbWFudWFsIG9yIGNoZWNrIG91dCB0aGlzIG9ubGluZSB0dXRvcmlhbCAoaHR0cHM6Ly9rYXRldG8ubmV0L25ldHdvcmstdmlzdWFsaXphdGlvbikgZm9yIGluc3BpcmF0aW9uLiAgCgpgYGB7ciBsYXlvdXRzfQpwYXIobWFyPWMoMCwwLDAsMCkrLjQsIG1mcm93ID0gYygxLDIpKSAjIHJlZHVjZSBtYXJnaW5zIGFuZCBwbG90IGJvdGggbmV0d29ya3MgdG9nZXRoZXIKCnNldC5zZWVkKDEpIAoKcGxvdChnLCBsYXlvdXQgPSBsYXlvdXRfaW5fY2lyY2xlLCBtYWluID0gJ2NpcmNsZScpCnBsb3QoZywgbGF5b3V0ID0gbGF5b3V0X3dpdGhfZ2VtLCBtYWluID0gJ2dlbScpCmBgYAoKIyBBcHBlbmRpeDogTmV0d29yayBDb21wb25lbnRzIAoKSW4gdGhpcyBzZWN0aW9uLCB0aGUgZ29hbCBpcyB0byBpbnRyb2R1Y2UgdXNlZnVsIFIgY29kZSBmb3IgKGkpIGRldGVjdGluZyBpZiB5b3VyIG5ldHdvcmsgY29tcHJpc2VzIG9mIGEgc2luZ2xlIGNvbm5lY3RlZCBjb21wb25lbnQgb3IgbXVsdGlwbGUsIGFuZCAoaWkpIGV4dHJhY3RpbmcgdGhlIGxhcmdlc3QgY29ubmVjdGVkIGNvbXBvbmVudCBvZiB0aGUgbmV0d29yayAob3IgYW5vdGhlciBuZXR3b3JrIGNvbXBvbmVudCkgYXMgYSBuZXcgZ3JhcGggb2JqZWN0IGZvciBhZGRpdGlvbmFsIGFuYWx5c2lzLiAKCiMjIEhvdyBtYW55IGNvbXBvbmVudHMgZG9lcyBteSBuZXR3b3JrIGhhdmU/IAoKYGBge3J9CnBhcihtYXI9YygwLDAsMCwwKSsuMSkgIyByZWR1Y2UgbWFyZ2lucwoKc2V0LnNlZWQoODgpCmd6IDwtIHNhbXBsZV9nbnAobiA9IDIwLCBwID0gMC4xMCkKcGxvdChneikKCmd6X2NvbXAgPC0gY29tcG9uZW50cyhneikKCmd6X2NvbXAkbWVtYmVyc2hpcCAjIGNvbXBvbmVudCBtZW1iZXJzaGlwIApnel9jb21wJGNzaXplICMgY29tcG9uZW50IHNpemUgCmd6X2NvbXAkbm8gIyBudW1iZXIgb2YgY29tcG9uZW50cyAKYGBgCgojIyBIb3cgY2FuIEkgZXh0cmFjdCBhIHNwZWNpZmljIG5ldHdvcmsgY29tcG9uZW50IGFzIGEgbmV3IG5ldHdvcmsgb2JqZWN0PyAKCldlIGNhbiB1c2UgdGhlIGBpbmR1Y2VkX3N1YmdyYXBoYCBmdW5jdGlvbiB0byBjcmVhdGUgInN1YnNldHMiIG9mIGEgbmV0d29yayBieSBzZWxlY3RpbmcgdGhlIG5vZGVzIHRoYXQgeW91IHdpc2ggdG8ga2VlcC4gVGhlc2Ugbm9kZXMgYW5kIGFsbCB0aGUgZWRnZXMgYW1vbmcgdGhlbSB3aWxsIGJlIHJldGFpbmVkIGluIHRoZSBuZXcgbmV0d29yayBvYmplY3QuIAoKYGBge3J9CnBhcihtYXI9YygwLDAsMCwwKSsuMSkgIyByZWR1Y2UgbWFyZ2lucwoKIyBnel9jb21wIDwtIGNvbXBvbmVudHMoZ3opCmd6X2xjYyA8LSBpbmR1Y2VkX3N1YmdyYXBoKGdyYXBoID0gZ3osIAogICAgICAgICAgICAgICAgICAgICAgICAgICB2aWRzID0gZ3pfY29tcCRtZW1iZXJzaGlwID09IHdoaWNoLm1heChnel9jb21wJGNzaXplKSAjIGEgVC9GIHZlY3RvciBpbmRpY2F0aW5nIHRoZSBub2RlcyB3aG9zZSBjb21wb25lbnQgbWVtYmVyc2hpcCBpcyB0aGUgc2FtZSBhcyB0aGUgbGFyZ2VzdCBjb21wb25lbnQgLSB3ZSBjYW4gZ2V0IHRoaXMgaW5mb3JtYXRpb24gZnJvbSB0aGUgY29tcG9uZW50cyBvYmplY3QgYWJvdmUKICAgICAgICAgICAgICAgICAgICAgICAgICAgKQoKcGxvdChnel9sY2MpCgojIHlvdSBjYW4gc3BlY2lmeSBhbnkgY29tcG9uZW50IHNpemUgeW91IHdpc2gKZ3pfaGVybWl0IDwtIGluZHVjZWRfc3ViZ3JhcGgoZ3JhcGggPSBneiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHZpZHMgPSBnel9jb21wJG1lbWJlcnNoaXAgPT0gMyAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKQoKcGxvdChnel9oZXJtaXQpCmBgYAoKIyBBZGRpdGlvbmFsIFJlc291cmNlcyAKCk9nbnlhbm92YSwgSy4gKDIwMjEpIE5ldHdvcmsgdmlzdWFsaXphdGlvbiB3aXRoIFIuIFJldHJpZXZlZCBmcm9tIHd3dy5rYXRldG8ubmV0L25ldHdvcmstdmlzdWFsaXphdGlvbi4gaHR0cHM6Ly9rYXRldG8ubmV0L25ldHdvcmstdmlzdWFsaXphdGlvbgoKVGhlIG9mZmljaWFsIGBpZ3JhcGhgIG1hbnVhbCAodi4xLjMuNCkuIGh0dHBzOi8vaWdyYXBoLm9yZy9yL2RvYy8gCgpHZXBoaTogQSBtdWx0aS1wbGF0Zm9ybSwgZnJlZSB0byBkb3dubG9hZCBHVUkgYXBwIGZvciBuZXR3b3JrIGFuYWx5c2lzIGFuZCB2aXN1YWxpemF0aW9uLiBodHRwczovL2dlcGhpLm9yZy8KCiMgUmVmZXJlbmNlcyAKCkJhcnJhdCwgQS4sIEJhcnRow6lsZW15LCBNLiwgUGFzdG9yLVNhdG9ycmFzLCBSLiwgJiBWZXNwaWduYW5pLCBBLiAoMjAwNCkuIFRoZSBhcmNoaXRlY3R1cmUgb2YgY29tcGxleCB3ZWlnaHRlZCBuZXR3b3Jrcy4gUHJvY2VlZGluZ3Mgb2YgdGhlIE5hdGlvbmFsIEFjYWRlbXkgb2YgU2NpZW5jZXMsIDEwMSgxMSksIDM3NDfigJMzNzUyLiBodHRwczovL2RvaS5vcmcvMTAuMTA3My9wbmFzLjA0MDAwODcxMDEKCkJsb25kZWwsIFYuIEQuLCBHdWlsbGF1bWUsIEouIEwuLCBMYW1iaW90dGUsIFIuLCAmIExlZmVidnJlLCBFLiAoMjAwOCkuIEZhc3QgdW5mb2xkaW5nIG9mIGNvbW11bml0aWVzIGluIGxhcmdlIG5ldHdvcmtzLiBKb3VybmFsIG9mIFN0YXRpc3RpY2FsIE1lY2hhbmljczogVGhlb3J5IGFuZCBFeHBlcmltZW50LCAyMDA4KDEwKSwgUDEwMDA4LgoKRGUgRGV5bmUsIFMuLCBOYXZhcnJvLCBELiBKLiwgUGVyZm9ycywgQS4sIEJyeXNiYWVydCwgTS4sICYgU3Rvcm1zLCBHLiAoMjAxOSkuIFRoZSDigJxTbWFsbCBXb3JsZCBvZiBXb3Jkc+KAnSBFbmdsaXNoIHdvcmQgYXNzb2NpYXRpb24gbm9ybXMgZm9yIG92ZXIgMTIsMDAwIGN1ZSB3b3Jkcy4gQmVoYXZpb3IgUmVzZWFyY2ggTWV0aG9kcywgNTEsIDk4N+KAkzEwMDYuCgpGb3J0dW5hdG8sIFMuICgyMDEwKS4gQ29tbXVuaXR5IGRldGVjdGlvbiBpbiBncmFwaHMuIFBoeXNpY3MgUmVwb3J0cywgNDg2KDMtNSksIDc1LTE3NC4KCkdpcnZhbiwgTS4sICYgTmV3bWFuLCBNLiBFLiAoMjAwMikuIENvbW11bml0eSBzdHJ1Y3R1cmUgaW4gc29jaWFsIGFuZCBiaW9sb2dpY2FsIG5ldHdvcmtzLiBQcm9jZWVkaW5ncyBvZiB0aGUgTmF0aW9uYWwgQWNhZGVteSBvZiBTY2llbmNlcywgOTkoMTIpLCA3ODIxLTc4MjYuCgpIdW1waHJpZXMsIE0uIEQuLCAmIEd1cm5leSwgSy4gKDIwMDgpLiBOZXR3b3JrIOKAmHNtYWxsLXdvcmxkLW5lc3PigJk6IEEgcXVhbnRpdGF0aXZlIG1ldGhvZCBmb3IgZGV0ZXJtaW5pbmcgY2Fub25pY2FsIG5ldHdvcmsgZXF1aXZhbGVuY2UuIFBsb1MgT25lLCAzKDQpLgoKTHVjZSwgUC4gQS4sICYgUGlzb25pLCBELiBCLiAoMTk5OCkuIFJlY29nbml6aW5nIHNwb2tlbiB3b3JkczogVGhlIE5laWdoYm9yaG9vZCBBY3RpdmF0aW9uIE1vZGVsLiBFYXIgYW5kIEhlYXJpbmcsIDE5KDEpLCAx4oCTMzYuCgpOZWFsLCBaLiBQLiAoMjAxNykuIEhvdyBzbWFsbCBpcyBpdD8gQ29tcGFyaW5nIGluZGljZXMgb2Ygc21hbGwgd29ybGRsaW5lc3MuIE5ldHdvcmsgU2NpZW5jZSwgNSgxKSwgMzDigJM0NC4gaHR0cHM6Ly9kb2kub3JnLzEwLjEwMTcvbndzLjIwMTcuNQoKTmV3bWFuLCBNLiBFLiAoMjAwNikuIE1vZHVsYXJpdHkgYW5kIGNvbW11bml0eSBzdHJ1Y3R1cmUgaW4gbmV0d29ya3MuIFByb2NlZWRpbmdzIG9mIHRoZSBOYXRpb25hbCBBY2FkZW15IG9mIFNjaWVuY2VzLCAxMDMoMjMpLCA4NTc3LTg1ODIuICAgCgpQb25zLCBQLiwgJiBMYXRhcHksIE0uICgyMDA1LCBPY3RvYmVyKS4gQ29tcHV0aW5nIGNvbW11bml0aWVzIGluIGxhcmdlIG5ldHdvcmtzIHVzaW5nIHJhbmRvbSB3YWxrcy4gSW4gSW50ZXJuYXRpb25hbCBzeW1wb3NpdW0gb24gY29tcHV0ZXIgYW5kIGluZm9ybWF0aW9uIHNjaWVuY2VzIChwcC4gMjg0LTI5MykuIFNwcmluZ2VyLCBCZXJsaW4sIEhlaWRlbGJlcmcuCgpWaXRldml0Y2gsIE0uIFMuICgyMDA4KS4gV2hhdCBjYW4gZ3JhcGggdGhlb3J5IHRlbGwgdXMgYWJvdXQgd29yZCBsZWFybmluZyBhbmQgbGV4aWNhbCByZXRyaWV2YWw/IEpvdXJuYWwgb2YgU3BlZWNoLCBMYW5ndWFnZSwgYW5kIEhlYXJpbmcgUmVzZWFyY2gsIDUxKDIpLCA0MDjigJM0MjIuIGh0dHBzOi8vZG9pLm9yZy8xMC4xMDQ0LzEwOTItNDM4OCgyMDA4LzAzMCkK

Copyright © 2022 CSQ Siew. All rights reserved.

Creative Commons License
This work is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License.