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-compcog.RData')
There should be 3 objects loaded into your R workspace:
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., /k@t/–/k@p/ 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.
pnet_edgelist
is the raw data that is used to create
pnet
. It is an edgelist whereby each row depicts one edge
in the network.
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 https://smallworldofwords.org/en/project/research for
more information.
summary(pnet)
## IGRAPH ddad942 UN-- 39 121 --
## + attr: name (v/c)
summary(snet)
## IGRAPH 9a00b73 DNW- 211 1324 --
## + attr: name (v/c), weight (e/n)
Notice the two numbers on the first row: The first tells you the
number of nodes or vertices in the network, the second tells you the
number of edges or links.
There are also some cyptic looking characters. Let’s unpack that. The
first character is either “U” or “D” and tells you whether the edges are
“undirected” or “directed”. The second character “N” (if present) tells
you that the nodes have “names”. The third character “W” (if present)
tells you that the edges are weighted. The final character is blank
unless you have a special type of network known as a bipartite network
(in which case the fourth character is “B”).
On the second row, additional information about node or edge
attributes can be found if they have been specified in the construction
of the network. Both networks have node names, and the semantic network
has an edge attribute known as weight
.
Loading a network into igraph
There are a number of different ways to convert your raw data into a
network representation that igraph
can analyze. This data
first needs to be organized as an edgelist or an adjacency matrix or an
adjacency list and then loaded into RStudio. Below I provide a quick
example of how an edgelist can be converted into a
network.
head(pnet_edgelist)
## [,1] [,2]
## [1,] "biC;beach" "iC;each"
## [2,] "biC;beach" "liC;leach"
## [3,] "iC;each" "liC;leach"
## [4,] "pi;p" "pis;peace"
## [5,] "biC;beach" "piC;peach"
## [6,] "iC;each" "piC;peach"
pnet2 <- graph_from_edgelist(pnet_edgelist,
directed = F)
summary(pnet2)
## IGRAPH 2e75c97 UN-- 39 121 --
## + attr: name (v/c)
There are many useful functions in igraph
(those that
begin with graph_from_*
) for converting various kinds of
datasets into networks. The igraph
manual is a good place
to start learning about these functions and their arguments to specify
edge or node attributes of the network. Outside of igraph
you will probably have to spend some time curating and wrangling your
data so that it is in the right formats for igraph
.
Visualizations
Here are visualizations of the networks we will play around with
today.
plot(pnet,
vertex.frame.color = 'white',
vertex.label.color = 'grey30',
edge.color = 'black',
vertex.label = gsub('[[:print:]]+;', '', V(pnet)$name),
layout = layout_with_lgl,
frame = TRUE,
margin = c(0,0,0,0),
main = 'A partial phonological network of English words')

plot(snet,
vertex.frame.color = 'white',
vertex.label.color = 'grey30',
edge.color = 'black',
vertex.color = 'seagreen',
vertex.size = 5,
edge.arrow.size = 0.4,
layout = layout_with_graphopt,
frame = TRUE,
margin = c(0,0,0,0),
main = 'A partial semantic network')

Disclaimers
Due to limited time, I assume that you have some familiarity with R
and RStudio. A gentle introduction to R programming can be found here:
https://psyr.djnavarro.net/
I also choose to not cover in detail a number of important topics
such as how to convert your raw data into an igraph
network
object, network visualization in igraph
(for a great
introduction to this topic, see https://kateto.net/network-visualization), and other
methods of quantifying the community structure of the network (i.e.,
meso-level). Some of these topics are briefly touched on in a different
tutorial and you can find the materials here: https://vpf-netsci.netlify.app/part2-demo.html
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.

degree(graph = pnet)
## 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 = snet, v = 'cheese')
## cheese
## 231
degree(graph = snet, v = 'cheese', mode = 'in')
## cheese
## 185
degree(graph = snet, v = 'cheese', mode = 'out')
## cheese
## 46
degree(graph = snet, v = 'cheese', mode = 'all')
## 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 = snet) |> head(5)
## age aged ages aging american
## 0.57 0.55 0.04 0.17 0.36
strength(graph = snet, v = 'age', mode = 'in')
## age
## 0.38
strength(graph = snet, v = 'age', mode = 'out')
## age
## 0.19
strength(graph = snet, v = 'age', mode = 'all')
## age
## 0.57
Relevant finding in psychology: De Deyne et al. (2019) found that
strength (operationalized as association frequency) was superior to, or
at least similar to, word frequency as a predictor of lexical decision
and semantic decision performance.
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.
Relevant finding in psychology: Chan & Vitevitch (2009) showed
that words with higher clustering coefficients were responded to more
slowly than words with lower clustering coefficient in spoken word
recognition tasks (even after controling for the size of the
phonological neighborhood.
transitivity(graph = pnet, type = 'local', vids = 'spik;speak') |> round(3)
## [1] 0.218
A couple of things to note:
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)
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.
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).
transitivity(graph = snet, type = 'local', vids = 'cheese') |> round(3)
## [1] 0.043
transitivity(graph = snet, type = 'weighted', vids = 'cheese') |> round(3)
## [1] 0.056
transitivity(graph = pnet, type = 'local', vids = 'spik;speak') |> round(3)
## [1] 0.218
transitivity(graph = pnet, 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(graph = snet, 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 = snet, 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 = snet, 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
closeness(graph = snet, normalized = T, mode = 'all') |> head()
## 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 (so that a path exists between any pair of nodes). 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).
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 rather than
connection strengths: 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.
Relevant finding in psychology: Siew (2018) showed that the
closeness centrality of words in an orthographic similarity network
influenced word naming and visual lexical decision performance
differently. Words of higher closeness centralities were responded to
more quickly in lexical decision, but were named more slowly.
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.
betweenness(graph = pnet, normalized = T, weights = NA, directed = F) |> head()
## xpik;apeak biC;beach sid;cede iC;each liC;leach pi;p
## 0.00000000 0.00000000 0.01730678 0.00000000 0.00000000 0.00000000
betweenness(graph = snet, normalized = T, weights = NULL, directed = T) |> head()
## age aged ages aging american appetizer
## 0.0002050581 0.0009881305 0.0000000000 0.0001367054 0.0103941672 0.0098454470
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 (simulating
“boredom” of the surfer). 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.
page_rank(graph = pnet, directed = F, weights = NA)$vector |> head()
## xpik;apeak biC;beach sid;cede iC;each liC;leach pi;p
## 0.01085805 0.02109911 0.02350068 0.02109911 0.02109911 0.02849254
page_rank(graph = snet, directed = T, weights = NULL)$vector |> head()
## age aged ages aging american appetizer
## 0.0016034240 0.0012433152 0.0007545644 0.0009712994 0.0011928822 0.0008034249
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.
Relevant finding in psychology: Griffiths et al. (2007) showed that
Page Rank centralities of words in a word association network provided
good predictions for the words generated by participants in a letter
fluency task.
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.
average.path.length(graph = pnet)
## [1] 2.557355
mean_distance(graph = pnet)
## [1] 2.557355
mean_distance(graph = snet, weights = NULL, directed = T)
## [1] 0.1090017
mean_distance(graph = snet, weights = NULL, directed = F)
## [1] 0.06239856
mean_distance(graph = snet, weights = NA, directed = T)
## [1] 2.926504
Relevant finding in psychology: Siew (2018) showed that concept
networks (constructed from concept maps generated by students) with
larger average shortest path lengths were associated with higher quiz
scores, after controlling for network size.
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 = pnet, type = 'global')
## [1] 0.6805869
transitivity(graph = snet, 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 = pnet)
## [1] 0.1632928
graph.density(graph = snet)
## [1] 0.02988039
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
diameter(graph = pnet)
## [1] 4
diameter(graph = snet, directed = T, weights = NULL)
## [1] 0.88
diameter(graph = snet, directed = F, weights = NULL)
## [1] 0.56
diameter(graph = snet, directed = T, weights = NA)
## [1] 7
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.
Chan, K. Y., & Vitevitch, M. S. (2009). The influence of the
phonological neighborhood clustering coefficient on spoken word
recognition. Journal of Experimental Psychology: Human Perception and
Performance, 35(6), 1934–1949. https://doi.org/10.1037/a0016902
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.
Humphries, M. D., & Gurney, K. (2008). Network
‘small-world-ness’: A quantitative method for determining canonical
network equivalence. PloS One, 3(4).
Griffiths, T. L., Steyvers, M., & Firl, A. (2007). Google and the
Mind: Predicting Fluency With PageRank. Psychological Science, 18(12),
1069–1076. https://doi.org/10.1111/j.1467-9280.2007.02027.x
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.
Siew, C. S. Q. (2013). Community structure in the phonological
network. Frontiers in Psychology, 4, 553.
Siew, C. S. Q. (2018). The orthographic similarity structure of
English words: Insights from network science. Applied Network Science,
3(1), 13.
Siew, C. S. Q. (2018). Using network science to analyze concept maps
of psychology undergraduates. Applied Cognitive Psychology.
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)
LS0tCnRpdGxlOiAiQ29tcENvZyAyMDIzIE5ldHdvcmsgQW5hbHlzaXMgVHV0b3JpYWwiCnN1YnRpdGxlOiAiTW9kZWxpbmcgcHN5Y2hvbG9naWNhbCBkYXRhIHdpdGggY29nbml0aXZlIG5ldHdvcmtzIgphdXRob3I6ICJDeW50aGlhIFNpZXciCmRhdGU6ICIxc3QgRmViIDIwMjMiCm91dHB1dDoKICBodG1sX2RvY3VtZW50OgogICAgdG9jOiBUUlVFCiAgICB0b2NfZmxvYXQ6IFRSVUUKICAgIGRmX3ByaW50OiBwYWdlZAogICAgY29kZV9kb3dubG9hZDogdHJ1ZQotLS0KIyBTZXQgdXAgCgpgYGB7ciBzZXQtdXB9CmxpYnJhcnkoaWdyYXBoKQoKbG9hZCgnZGF0YS9leGFtcGxlLWRhdGEtY29tcGNvZy5SRGF0YScpCmBgYAoKVGhlcmUgc2hvdWxkIGJlIDMgb2JqZWN0cyBsb2FkZWQgaW50byB5b3VyIFIgd29ya3NwYWNlOgoKLSBgcG5ldGAKLSBgc25ldGAgCi0gYHBuZXRfZWRnZWxpc3RgCgpgcG5ldGAgcmVmZXJzIHRvIGEgc2VjdGlvbiBvZiB0aGUgcGhvbm9sb2dpY2FsIG5ldHdvcmsgZmlyc3QgZGVzY3JpYmVkIGJ5IFZpdGV2aXRjaCAoMjAwOCkuIFRoZSBub2RlcyByZXByZXNlbnQgRW5nbGlzaCB3b3JkcywgYW5kIGVkZ2VzIGNvbm5lY3Qgd29yZHMgdGhhdCBhcmUgcGhvbm9sb2dpY2FsIG5laWdoYm9ycyBvZiBlYWNoIG90aGVyIGJhc2VkIG9uIHRoZSAxLWVkaXQgZGlzdGFuY2UgbWV0cmljIGNvbXB1dGVkIG9uIHRoZWlyIHBob25vbG9naWNhbCB0cmFuc2NyaXB0aW9ucyAoTHVjZSAmIFBpc29uaSwgMTk5OCkuIEUuZy4sIC9rXEB0Ly0tL2tcQHAvIGFyZSBuZWlnaGJvcmluZyBub2Rlcy4gU3BlY2lmaWNhbGx5LCB0aGlzIG5ldHdvcmsgaXMgdGhlIDItaG9wIG5ldHdvcmsgb2YgdGhlIHdvcmQgJ3NwZWVjaCcgLSBpbiBhZGRpdGlvbiB0byAnc3BlZWNoJyBpdHNlbGYsIGl0cyBpbW1lZGlhdGUgcGhvbm9sb2dpY2FsIG5laWdoYm9ycyBhbmQgdGhlIG5laWdoYm9ycyBvZiBpdHMgbmVpZ2hib3JzIGFyZSBpbmNsdWRlZCBpbiB0aGlzIHJlcHJlc2VudGF0aW9uLgoKYHBuZXRfZWRnZWxpc3RgIGlzIHRoZSByYXcgZGF0YSB0aGF0IGlzIHVzZWQgdG8gY3JlYXRlIGBwbmV0YC4gSXQgaXMgYW4gZWRnZWxpc3Qgd2hlcmVieSBlYWNoIHJvdyBkZXBpY3RzIG9uZSBlZGdlIGluIHRoZSBuZXR3b3JrLgoKYHNuZXRgIHJlZmVycyB0byBhIHNlY3Rpb24gb2YgdGhlIHdvcmQgYXNzb2NpYXRpb24gbmV0d29yayB1c2luZyBkYXRhIGZyb20gRGUgRGV5bmUgZXQgYWwuICgyMDIwKS4gVGhlIG5vZGVzIHJlcHJlc2VudCBFbmdsaXNoIHdvcmRzLCBhbmQgZWRnZXMgY29ubmVjdCB3b3JkcyB0aGF0IGFyZSBwcm9kdWNlZCBhcyBmcmVlIGFzc29jaWF0aW9ucyBvZiBvdGhlciB3b3Jkcy4gRS5nLiwgImNhdCItLSJkb2ciIGFyZSBuZWlnaGJvcmluZyBub2Rlcy4gVGhpcyBuZXR3b3JrIGhhcyBlZGdlcyB3aXRoIDIgaW50ZXJlc3RpbmcgcHJvcGVydGllcy4gKGkpIEVhY2ggZWRnZSBoYXMgYSBgd2VpZ2h0YCBhdHRyaWJ1dGUgdGhhdCBjb3JyZXNwb25kcyB0byB0aGUgYXNzb2NpYXRpdmUgc3RyZW5ndGggb2YgdHdvIG5vZGVzLCBvciB0aGUgcHJvcG9ydGlvbiBvZiBwYXJ0aWNpcGFudHMgd2hvIHByb3ZpZGVkIGEgc3BlY2lmaWMgcmVzcG9uc2UgdG8gdGhlIGN1ZSB3b3JkLiAoaWkpIFRoZSBlZGdlcyBhcmUgYWxzbyBgZGlyZWN0ZWRgIHN1Y2ggdGhlIGRpcmVjdGlvbiBnb2VzICpmcm9tKiB0aGUgY3VlIHdvcmQgKnRvKiB0aGUgcmVzcG9uc2Ugd29yZCwgaS5lLiwgImNhdCItPiJkb2ciLiBTcGVjaWZpY2FsbHksIHRoaXMgbmV0d29yayBpcyB0aGUgMS1ob3AgbmV0d29yayBvZiB0aGUgd29yZCAnY2hlZXNlJyAtIGluIGFkZGl0aW9uIHRvICdjaGVlc2UnIGl0c2VsZiwgaXRzIGltbWVkaWF0ZSBhc3NvY2lhdGVzIGFuZCB0aGUgY3VlIHdvcmRzIHRoYXQgbGVkIHRvIHRoZSByZXNwb25zZSAnY2hlZXNlJyBhcmUgaW5jbHVkZWQgaW4gdGhpcyByZXByZXNlbnRhdGlvbi4gU2VlIGh0dHBzOi8vc21hbGx3b3JsZG9md29yZHMub3JnL2VuL3Byb2plY3QvcmVzZWFyY2ggZm9yIG1vcmUgaW5mb3JtYXRpb24uIAoKYGBge3IgcHJldmlld30Kc3VtbWFyeShwbmV0KSAjIHVuZGlyZWN0ZWQsIHVud2VpZ2h0ZWQgZWRnZXMgCgpzdW1tYXJ5KHNuZXQpICMgZGlyZWN0ZWQsIHdlaWdodGVkIGVkZ2VzCmBgYAoKTm90aWNlIHRoZSB0d28gbnVtYmVycyBvbiB0aGUgZmlyc3Qgcm93OiBUaGUgZmlyc3QgdGVsbHMgeW91IHRoZSBudW1iZXIgb2Ygbm9kZXMgb3IgdmVydGljZXMgaW4gdGhlIG5ldHdvcmssIHRoZSBzZWNvbmQgdGVsbHMgeW91IHRoZSBudW1iZXIgb2YgZWRnZXMgb3IgbGlua3MuIAoKVGhlcmUgYXJlIGFsc28gc29tZSBjeXB0aWMgbG9va2luZyBjaGFyYWN0ZXJzLiBMZXQncyB1bnBhY2sgdGhhdC4gVGhlIGZpcnN0IGNoYXJhY3RlciBpcyBlaXRoZXIgIlUiIG9yICJEIiBhbmQgdGVsbHMgeW91IHdoZXRoZXIgdGhlIGVkZ2VzIGFyZSAidW5kaXJlY3RlZCIgb3IgImRpcmVjdGVkIi4gVGhlIHNlY29uZCBjaGFyYWN0ZXIgIk4iIChpZiBwcmVzZW50KSB0ZWxscyB5b3UgdGhhdCB0aGUgbm9kZXMgaGF2ZSAibmFtZXMiLiBUaGUgdGhpcmQgY2hhcmFjdGVyICJXIiAoaWYgcHJlc2VudCkgdGVsbHMgeW91IHRoYXQgdGhlIGVkZ2VzIGFyZSB3ZWlnaHRlZC4gVGhlIGZpbmFsIGNoYXJhY3RlciBpcyBibGFuayB1bmxlc3MgeW91IGhhdmUgYSBzcGVjaWFsIHR5cGUgb2YgbmV0d29yayBrbm93biBhcyBhIGJpcGFydGl0ZSBuZXR3b3JrIChpbiB3aGljaCBjYXNlIHRoZSBmb3VydGggY2hhcmFjdGVyIGlzICJCIikuIAoKT24gdGhlIHNlY29uZCByb3csIGFkZGl0aW9uYWwgaW5mb3JtYXRpb24gYWJvdXQgbm9kZSBvciBlZGdlIGF0dHJpYnV0ZXMgY2FuIGJlIGZvdW5kIGlmIHRoZXkgaGF2ZSBiZWVuIHNwZWNpZmllZCBpbiB0aGUgY29uc3RydWN0aW9uIG9mIHRoZSBuZXR3b3JrLiBCb3RoIG5ldHdvcmtzIGhhdmUgbm9kZSBuYW1lcywgYW5kIHRoZSBzZW1hbnRpYyBuZXR3b3JrIGhhcyBhbiBlZGdlIGF0dHJpYnV0ZSBrbm93biBhcyBgd2VpZ2h0YC4gICAKCiMjIExvYWRpbmcgYSBuZXR3b3JrIGludG8gYGlncmFwaGAKClRoZXJlIGFyZSBhIG51bWJlciBvZiBkaWZmZXJlbnQgd2F5cyB0byBjb252ZXJ0IHlvdXIgcmF3IGRhdGEgaW50byBhIG5ldHdvcmsgcmVwcmVzZW50YXRpb24gdGhhdCBgaWdyYXBoYCBjYW4gYW5hbHl6ZS4gVGhpcyBkYXRhIGZpcnN0IG5lZWRzIHRvIGJlIG9yZ2FuaXplZCBhcyBhbiBlZGdlbGlzdCBvciBhbiBhZGphY2VuY3kgbWF0cml4IG9yIGFuIGFkamFjZW5jeSBsaXN0IGFuZCB0aGVuIGxvYWRlZCBpbnRvIFJTdHVkaW8uIEJlbG93IEkgcHJvdmlkZSBhIHF1aWNrIGV4YW1wbGUgb2YgaG93IGFuICoqZWRnZWxpc3QqKiBjYW4gYmUgY29udmVydGVkIGludG8gYSBuZXR3b3JrLiAKCmBgYHtyfQpoZWFkKHBuZXRfZWRnZWxpc3QpICMgcHJldmlldyBmaXJzdCBmZXcgcm93cyBvZiB0aGUgZWRnZWxpc3QgCgpwbmV0MiA8LSBncmFwaF9mcm9tX2VkZ2VsaXN0KHBuZXRfZWRnZWxpc3QsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZGlyZWN0ZWQgPSBGKSAjIHRoZSBlZGdlcyBhcmUgdW5kaXJlY3RlZCBpbiB0aGlzIG5ldHdvcmsgCgpzdW1tYXJ5KHBuZXQyKQpgYGAKClRoZXJlIGFyZSBtYW55IHVzZWZ1bCBmdW5jdGlvbnMgaW4gYGlncmFwaGAgKHRob3NlIHRoYXQgYmVnaW4gd2l0aCBgZ3JhcGhfZnJvbV8qYCkgZm9yIGNvbnZlcnRpbmcgdmFyaW91cyBraW5kcyBvZiBkYXRhc2V0cyBpbnRvIG5ldHdvcmtzLiBUaGUgYGlncmFwaGAgbWFudWFsIGlzIGEgZ29vZCBwbGFjZSB0byBzdGFydCBsZWFybmluZyBhYm91dCB0aGVzZSBmdW5jdGlvbnMgYW5kIHRoZWlyIGFyZ3VtZW50cyB0byBzcGVjaWZ5IGVkZ2Ugb3Igbm9kZSBhdHRyaWJ1dGVzIG9mIHRoZSBuZXR3b3JrLiBPdXRzaWRlIG9mIGBpZ3JhcGhgIHlvdSB3aWxsIHByb2JhYmx5IGhhdmUgdG8gc3BlbmQgc29tZSB0aW1lIGN1cmF0aW5nIGFuZCB3cmFuZ2xpbmcgeW91ciBkYXRhIHNvIHRoYXQgaXQgaXMgaW4gdGhlIHJpZ2h0IGZvcm1hdHMgZm9yIGBpZ3JhcGhgLiAKCiMjIFZpc3VhbGl6YXRpb25zIAoKSGVyZSBhcmUgdmlzdWFsaXphdGlvbnMgb2YgdGhlIG5ldHdvcmtzIHdlIHdpbGwgcGxheSBhcm91bmQgd2l0aCB0b2RheS4gCgpgYGB7cn0KcGxvdChwbmV0LAogICAgIHZlcnRleC5mcmFtZS5jb2xvciA9ICd3aGl0ZScsCiAgICAgdmVydGV4LmxhYmVsLmNvbG9yID0gJ2dyZXkzMCcsCiAgICAgZWRnZS5jb2xvciA9ICdibGFjaycsCiAgICAgdmVydGV4LmxhYmVsID0gZ3N1YignW1s6cHJpbnQ6XV0rOycsICcnLCBWKHBuZXQpJG5hbWUpLCAjIHJlZ2V4IHRvIG9ubHkgcHJpbnQgdGhlIHdvcmQgbmFtZXMgYW5kIG5vdCB0cmFuc2NyaXB0aW9ucwogICAgIGxheW91dCA9IGxheW91dF93aXRoX2xnbCwKICAgICBmcmFtZSA9IFRSVUUsCiAgICAgbWFyZ2luID0gYygwLDAsMCwwKSwKICAgICBtYWluID0gJ0EgcGFydGlhbCBwaG9ub2xvZ2ljYWwgbmV0d29yayBvZiBFbmdsaXNoIHdvcmRzJykKYGBgCgpgYGB7cn0KcGxvdChzbmV0LAogICAgIHZlcnRleC5mcmFtZS5jb2xvciA9ICd3aGl0ZScsCiAgICAgdmVydGV4LmxhYmVsLmNvbG9yID0gJ2dyZXkzMCcsCiAgICAgZWRnZS5jb2xvciA9ICdibGFjaycsCiAgICAgdmVydGV4LmNvbG9yID0gJ3NlYWdyZWVuJywKICAgICB2ZXJ0ZXguc2l6ZSA9IDUsCiAgICAgZWRnZS5hcnJvdy5zaXplID0gMC40LAogICAgIGxheW91dCA9IGxheW91dF93aXRoX2dyYXBob3B0LAogICAgIGZyYW1lID0gVFJVRSwKICAgICBtYXJnaW4gPSBjKDAsMCwwLDApLAogICAgIG1haW4gPSAnQSBwYXJ0aWFsIHNlbWFudGljIG5ldHdvcmsnKQpgYGAKCiMjIERpc2NsYWltZXJzCgpEdWUgdG8gbGltaXRlZCB0aW1lLCBJIGFzc3VtZSB0aGF0IHlvdSBoYXZlIHNvbWUgZmFtaWxpYXJpdHkgd2l0aCBSIGFuZCBSU3R1ZGlvLiBBIGdlbnRsZSBpbnRyb2R1Y3Rpb24gdG8gUiBwcm9ncmFtbWluZyBjYW4gYmUgZm91bmQgaGVyZTogaHR0cHM6Ly9wc3lyLmRqbmF2YXJyby5uZXQvCgpJIGFsc28gY2hvb3NlIHRvIG5vdCBjb3ZlciBpbiBkZXRhaWwgYSBudW1iZXIgb2YgaW1wb3J0YW50IHRvcGljcyBzdWNoIGFzIGhvdyB0byBjb252ZXJ0IHlvdXIgcmF3IGRhdGEgaW50byBhbiBgaWdyYXBoYCBuZXR3b3JrIG9iamVjdCwgbmV0d29yayB2aXN1YWxpemF0aW9uIGluIGBpZ3JhcGhgIChmb3IgYSBncmVhdCBpbnRyb2R1Y3Rpb24gdG8gdGhpcyB0b3BpYywgc2VlIGh0dHBzOi8va2F0ZXRvLm5ldC9uZXR3b3JrLXZpc3VhbGl6YXRpb24pLCBhbmQgb3RoZXIgbWV0aG9kcyBvZiBxdWFudGlmeWluZyB0aGUgY29tbXVuaXR5IHN0cnVjdHVyZSBvZiB0aGUgbmV0d29yayAoaS5lLiwgbWVzby1sZXZlbCkuIFNvbWUgb2YgdGhlc2UgdG9waWNzIGFyZSBicmllZmx5IHRvdWNoZWQgb24gaW4gYSBkaWZmZXJlbnQgdHV0b3JpYWwgYW5kIHlvdSBjYW4gZmluZCB0aGUgbWF0ZXJpYWxzIGhlcmU6IGh0dHBzOi8vdnBmLW5ldHNjaS5uZXRsaWZ5LmFwcC9wYXJ0Mi1kZW1vLmh0bWwgCgojIE1lYXN1cmluZyB0aGUgbmV0d29yayAKCk9uY2Ugd2UgaGF2ZSBhIG5ldHdvcmsgcmVwcmVzZW50YXRpb24sIHRoZSB0b29scyBvZiBuZXR3b3JrIHNjaWVuY2UgY2FuIGJlIGFwcGxpZWQgdG8gYW5hbHl6ZSB0aGUgbmV0d29ya3MgaW4gZGlmZmVyZW50IHdheXMuIEluIHRoaXMgdHV0b3JpYWwgd2UgZm9jdXMgb24gYSAqZGVzY3JpcHRpdmUqIGFuYWx5c2lzIG9mIHRoZSBuZXR3b3JrIGFuZCByZXZpZXcgdmFyaW91cyBuZXR3b3JrIG1lYXN1cmVzIHRoYXQgY2FuIGJlIHVzZWQgdG8gZGVzY3JpYmUgb3IgcXVhbnRpZnkgbmV0d29yayBzdHJ1Y3R1cmUgYXQgdGhyZWUgZGlmZmVyZW50IGxldmVscyBvZiB0aGUgbmV0d29yazogdGhlIG1pY3JvLWxldmVsIChyZWZlcnJpbmcgdG8gdGhlIGxvY2FsIHN0cnVjdHVyZSBhbmQgb3RoZXIgcHJvcGVydGllcyBvZiBpbmRpdmlkdWFsIG5vZGVzKSwgdGhlIG1lc28tbGV2ZWwgKHN1Ymdyb3VwcyBvciBjbHVzdGVycyBvZiBub2RlcyksIGFuZCB0aGUgbWFjcm8tbGV2ZWwgKHJlZmVycmluZyB0byB0aGUgb3ZlcmFsbCBvciBnbG9iYWwgc3RydWN0dXJlIG9mIHRoZSBuZXR3b3JrKS4gCgohW10oaHR0cHM6Ly93d3cubWRwaS5jb20vZWR1Y2F0aW9uL2VkdWNhdGlvbi0xMC0wMDEwMS9hcnRpY2xlX2RlcGxveS9odG1sL2ltYWdlcy9lZHVjYXRpb24tMTAtMDAxMDEtZzAwMS5wbmcpCgojIyBNaWNyby1sZXZlbCAobm9kZS1sZXZlbCkKCk1pY3JvLWxldmVsIG5ldHdvcmsgbWVhc3VyZXMgcHJvdmlkZSB5b3Ugd2l0aCBpbmZvcm1hdGlvbiBhYm91dCBzcGVjaWZpYyBub2RlcyBpbiB0aGUgbmV0d29yay4gVGhlc2UgYXJlIGdlbmVyYWxseSBrbm93biBhcyBjZW50cmFsaXR5IG1lYXN1cmVzIGluIHRoZSBuZXR3b3JrIHNjaWVuY2UgbGl0ZXJhdHVyZS4gQ2VudHJhbGl0eSBpcyB0aGUgbmV0d29yayBzY2llbnRpc3QncyB3YXkgb2YgcXVhbnRpZnlpbmcgdGhlIHJlbGF0aXZlICJpbXBvcnRhbmNlIiBvZiBhIGdpdmVuIG5vZGUgcmVsYXRpdmUgdG8gb3RoZXIgbm9kZXMgaW4gdGhlIG5ldHdvcmsuIFRoZXJlIGFyZSBbbWFueV0oaHR0cDovL3NjaG9jaGFzdGljcy5uZXQvc25hL3BlcmlvZGljLmh0bWwpIGRpZmZlcmVudCBkZWZpbml0aW9ucyBvZiB3aGF0IGNvdW50cyBhcyAiY2VudHJhbCIsIGFzIHlvdSB3aWxsIHNlZSBpbiB0aGUgZm9sbG93aW5nIHN1YnNlY3Rpb25zLiBUaGVyZSBpcyBubyBzaW5nbGUgImNvcnJlY3QiIG9yICJiZXN0IiBtZXRyaWMgLSB3aGljaCBtZXRyaWNzIGFyZSBtb3N0IHVzZWZ1bCB0byB5b3Ugd2lsbCBkZXBlbmQgb24gdGhlIG5hdHVyZSBvZiB0aGUgc3lzdGVtIHRoYXQgeW91IGFyZSBtb2RlbGluZyBhcyB3ZWxsIGFzIHRoZSBuZXR3b3JrIGJlaGF2aW9yIHRoYXQgeW91IGludGVyZXN0ZWQgaW4uIAoKIyMjIERlZ3JlZSAodW53ZWlnaHRlZCBlZGdlcykKClRoZSAqKmRlZ3JlZSoqIG9mIG5vZGUgKmkqIHJlZmVycyB0byB0aGUgbnVtYmVyIG9mIGVkZ2VzIG9yIGxpbmtzIGNvbm5lY3RlZCB0byB0aGF0IG5vZGUuCgpJZiB5b3VyIG5ldHdvcmsgaGFzIGRpcmVjdGVkIGVkZ2VzLCAqaW4tZGVncmVlKiByZWZlcnMgdG8gdGhlIG51bWJlciBvZiBlZGdlcyB0aGF0IGFyZSBnb2luZyB0b3dhcmRzIHRoZSB0YXJnZXQgbm9kZSwgd2hlcmVhcyAqb3V0LWRlZ3JlZSogcmVmZXJzIHRvIHRoZSBudW1iZXIgb2YgZWRnZXMgdGhhdCBhcmUgZ29pbmcgYXdheSBmcm9tIHRoZSB0YXJnZXQgbm9kZS4gCgohW10oaHR0cHM6Ly93d3cudGxhYi5pdC9lbi9hbGxlZ2F0aS9oZWxwX2VuX29ubGluZS90bGFiX2ltYWdlL2luX291dF9kZWdyZWUuanBnKQoKYGBge3J9CiMgdW5kaXJlY3RlZCBuZXR3b3JrCmRlZ3JlZShncmFwaCA9IHBuZXQpICMgZm9yIGFsbCBub2RlcyBpbiB0aGUgbmV0d29yawoKZGVncmVlKGdyYXBoID0gc25ldCwgdiA9ICdjaGVlc2UnKSAjIGZvciBhIHNwZWNpZmljIG5vZGUgaW4gdGhlIG5ldHdvcmsgCgojIGRpcmVjdGVkIG5ldHdvcmsKZGVncmVlKGdyYXBoID0gc25ldCwgdiA9ICdjaGVlc2UnLCBtb2RlID0gJ2luJykgIyBpbi1kZWdyZWUgPSBpbmNvbWluZyBlZGdlcwpkZWdyZWUoZ3JhcGggPSBzbmV0LCB2ID0gJ2NoZWVzZScsIG1vZGUgPSAnb3V0JykgIyBvdXQtZGVncmVlID0gb3V0Z29pbmcgZWRnZXMgCmRlZ3JlZShncmFwaCA9IHNuZXQsIHYgPSAnY2hlZXNlJywgbW9kZSA9ICdhbGwnKSAjIGluLWRlZ3JlZSArIG91dC1kZWdyZWUKYGBgCgojIyMgU3RyZW5ndGggKHdlaWdodGVkIGVkZ2VzKQoKVGhlICoqc3RyZW5ndGgqKiBvZiBub2RlICppKiByZWZlcnMgdG8gdGhlIHN1bSBvZiBpdHMgYWRqYWNlbnQgZWRnZSAqd2VpZ2h0cyouIE9ubHkgYXBwbGljYWJsZSB0byB3ZWlnaHRlZCBuZXR3b3Jrcy4gCgpgYGB7cn0Kc3RyZW5ndGgoZ3JhcGggPSBzbmV0KSB8PiBoZWFkKDUpCgojIGZvciBkaXJlY3RlZCBuZXR3b3JrcwpzdHJlbmd0aChncmFwaCA9IHNuZXQsIHYgPSAnYWdlJywgbW9kZSA9ICdpbicpICMgaW4tZGVncmVlID0gaW5jb21pbmcgZWRnZXMKc3RyZW5ndGgoZ3JhcGggPSBzbmV0LCB2ID0gJ2FnZScsIG1vZGUgPSAnb3V0JykgIyBvdXQtZGVncmVlID0gb3V0Z29pbmcgZWRnZXMgCnN0cmVuZ3RoKGdyYXBoID0gc25ldCwgdiA9ICdhZ2UnLCBtb2RlID0gJ2FsbCcpICMgaW4tZGVncmVlICsgb3V0LWRlZ3JlZQpgYGAKCjxwIHN0eWxlPSJjb2xvcjpibHVlOyI+KlJlbGV2YW50IGZpbmRpbmcgaW4gcHN5Y2hvbG9neTogRGUgRGV5bmUgZXQgYWwuICgyMDE5KSBmb3VuZCB0aGF0IHN0cmVuZ3RoIChvcGVyYXRpb25hbGl6ZWQgYXMgYXNzb2NpYXRpb24gZnJlcXVlbmN5KSB3YXMgc3VwZXJpb3IgdG8sIG9yIGF0IGxlYXN0IHNpbWlsYXIgdG8sIHdvcmQgZnJlcXVlbmN5IGFzIGEgcHJlZGljdG9yIG9mIGxleGljYWwgZGVjaXNpb24gYW5kIHNlbWFudGljIGRlY2lzaW9uIHBlcmZvcm1hbmNlLio8L3A+CgojIyMgTG9jYWwgQ2x1c3RlcmluZyBDb2VmZmljaWVudCAodW53ZWlnaHRlZCkKClRoZSAqKmxvY2FsIGNsdXN0ZXJpbmcgY29lZmZpY2llbnQqKiwgKkMqLCBvZiBub2RlICppKiBtZWFzdXJlcyB0aGUgcmF0aW8gb2YgdGhlIGFjdHVhbCBudW1iZXIgb2YgZWRnZXMgZXhpc3RpbmcgYW1vbmcgbm9kZXMgZGlyZWN0bHkgY29ubmVjdGVkIHRvIHRoZSB0YXJnZXQgbm9kZSAqaSogdG8gdGhlIG51bWJlciBvZiBhbGwgcG9zc2libGUgZWRnZXMgYW1vbmcgdGhlc2Ugbm9kZXMuIAoKKkMqIHJhbmdlcyBmcm9tIDAgdG8gMS4gV2hlbiAqQyogPSAwLCBub25lIG9mIHRoZSBuZWlnaGJvcnMgb2YgYSB0YXJnZXQgbm9kZSBhcmUgbmVpZ2hib3JzIG9mIGVhY2ggb3RoZXIuIFdoZW4gKkMqID0gMSwgZXZlcnkgbmVpZ2hib3IgaXMgYWxzbyBhIG5laWdoYm9yIG9mIGFsbCB0aGUgb3RoZXIgbmVpZ2hib3JzIG9mIGEgdGFyZ2V0IHdvcmQuCgpZb3UgY2FuIHRoaW5rIG9mIHRoZSBsb2NhbCBjbHVzdGVyaW5nIGNvZWZmaWNpZW50IGFzIHByb3ZpZGluZyBhIG1lYXN1cmUgb2YgdGhlIGxldmVsIG9mIGludGVyY29ubmVjdGl2aXR5IGFtb25nIHRoZSBsb2NhbCBuZWlnaGJvcmhvb2Qgb2YgdGhlIG5vZGUuIAoKIVtdKGltZy9oY2MtbGNjLmpwZykKCipCb3RoIHdvcmRzIGhhdmUgdGhlIHNhbWUgbnVtYmVyIG9mIG5laWdoYm9ycywgYnV0IGRpZmZlcmVudCBsb2NhbCBjbHVzdGVyaW5nIGNvZWZmaWNpZW50cy4qCgo8cCBzdHlsZT0iY29sb3I6Ymx1ZTsiPipSZWxldmFudCBmaW5kaW5nIGluIHBzeWNob2xvZ3k6IENoYW4gJiBWaXRldml0Y2ggKDIwMDkpIHNob3dlZCB0aGF0IHdvcmRzIHdpdGggaGlnaGVyIGNsdXN0ZXJpbmcgY29lZmZpY2llbnRzIHdlcmUgcmVzcG9uZGVkIHRvIG1vcmUgc2xvd2x5IHRoYW4gd29yZHMgd2l0aCBsb3dlciBjbHVzdGVyaW5nIGNvZWZmaWNpZW50IGluIHNwb2tlbiB3b3JkIHJlY29nbml0aW9uIHRhc2tzIChldmVuIGFmdGVyIGNvbnRyb2xpbmcgZm9yIHRoZSBzaXplIG9mIHRoZSBwaG9ub2xvZ2ljYWwgbmVpZ2hib3Job29kLio8L3A+CgpgYGB7cn0KdHJhbnNpdGl2aXR5KGdyYXBoID0gcG5ldCwgdHlwZSA9ICdsb2NhbCcsIHZpZHMgPSAnc3BpaztzcGVhaycpIHw+IHJvdW5kKDMpICMgZm9yIGEgc3BlY2lmaWMgbm9kZSBpbiB0aGUgbmV0d29yayAKCiMgaWYgeW91IGRvIG5vdCBzcGVjaWZ5IHRoZSB2aWRzIGFyZ3VtZW50IHRoZW4geW91IGdldCBhIHZhbHVlIGZvciBhbGwgbm9kZXMgaW4gdGhlIG5ldHdvcmssIG9yZGVyZWQgYnkgVihwbmV0KSRuYW1lIApgYGAKCkEgY291cGxlIG9mIHRoaW5ncyB0byBub3RlOgoKMS4gSXQgaXMgaW1wb3J0YW50IHRvIHNwZWNpZnkgYHR5cGUgPSBsb2NhbGAgZm9yIGxvY2FsIGNsdXN0ZXJpbmcgY29lZmZpY2llbnRzLCBhcyBjb21wYXJlZCB0byB0aGUgZ2xvYmFsIGNsdXN0ZXJpbmcgY29lZmZpY2llbnQgb2YgdGhlIGVudGlyZSBncmFwaCAodGhpcyBpcyBhIFttYWNyby1sZXZlbCBtZWFzdXJlXSgjZ2xvYmFsLWNsdXN0ZXJpbmctY29lZmZpY2llbnQpIHRoYXQgd2Ugd2lsbCB2aXNpdCBsYXRlcikgCgoyLiBNYW55IG9mIHRoZXNlIGZ1bmN0aW9ucyBjb250YWluIGFkZGl0aW9uYWwgYXJndW1lbnRzIGZvciBpbmRpY2F0aW5nIHdoZXRoZXIgdG8gY29uc2lkZXIgdGhlIGRpcmVjdGlvbmFsaXR5IGFuZCB3ZWlnaHRzIG9mIHRoZSBlZGdlcy4gSWYgeW91ciBncmFwaCBpcyB1bmRpcmVjdGVkIGFuZCB1bndlaWdodGVkLCB0aGVzZSBhcmUgaWdub3JlZCBieSBkZWZhdWx0LiBJZiB5b3VyIGdyYXBoIGlzIGRpcmVjdGVkIGFuZCB3ZWlnaHRlZCwgeW91IGNhbiBpbmRpY2F0ZSB3aGV0aGVyIHRvIGluY2x1ZGUgb3IgZXhjbHVkZSB0aGlzIGluZm9ybWF0aW9uIGZvciB0aGUgY29tcHV0YXRpb24gb2YgdGhlIG5ldHdvcmsgbWVhc3VyZS4KCiMjIyBMb2NhbCBDbHVzdGVyaW5nIENvZWZmaWNpZW50ICh3ZWlnaHRlZCkKCklmIHlvdSBoYXZlIGEgd2VpZ2h0ZWQgbmV0d29yaywgeW91IGNhbiBjb21wdXRlIGxvY2FsIGNsdXN0ZXJpbmcgY29lZmZpY2llbnRzIHVzaW5nIEJhcnJhdCBldCBhbC4ncyAoMjAwNCkgZ2VuZXJhbGl6YXRpb24gb2YgdHJhbnNpdGl2aXR5IHRvIHdlaWdodGVkIG5ldHdvcmtzIGJ5IHNwZWNpZnlpbmcgYHR5cGUgPSAnd2VpZ2h0ZWQnYC4gSWYgeW91ciBuZXR3b3JrIGlzIHVud2VpZ2h0ZWQsIHRoZSBnZW5lcmFsaXphdGlvbiB3aWxsIHJldHVybiB0aGUgdW53ZWlnaHRlZCBDIChzZWUgZXhhbXBsZSBvZiAnc3BlYWsnIGJlbG93KS4gCgpgYGB7cn0KIyB3ZWlnaHRlZCBuZXR3b3JrIAp0cmFuc2l0aXZpdHkoZ3JhcGggPSBzbmV0LCB0eXBlID0gJ2xvY2FsJywgdmlkcyA9ICdjaGVlc2UnKSB8PiByb3VuZCgzKSAKCnRyYW5zaXRpdml0eShncmFwaCA9IHNuZXQsIHR5cGUgPSAnd2VpZ2h0ZWQnLCB2aWRzID0gJ2NoZWVzZScpIHw+IHJvdW5kKDMpIAoKIyB1bndlaWdodGVkIG5ldHdvcmsgCnRyYW5zaXRpdml0eShncmFwaCA9IHBuZXQsIHR5cGUgPSAnbG9jYWwnLCB2aWRzID0gJ3NwaWs7c3BlYWsnKSB8PiByb3VuZCgzKQoKdHJhbnNpdGl2aXR5KGdyYXBoID0gcG5ldCwgdHlwZSA9ICd3ZWlnaHRlZCcsIHZpZHMgPSAnc3BpaztzcGVhaycpIHw+IHJvdW5kKDMpICMgbm8gZGlmZmVyZW5jZSB3aXRoIHRoZSBwcmV2aW91cwpgYGAKCiMjIyBDbG9zZW5lc3MgQ2VudHJhbGl0eSAKCkNsb3NlbmVzcyBjZW50cmFsaXR5IG9mIG5vZGUgKmkqIGlzIHRoZSBpbnZlcnNlIG9mIHRoZSBhdmVyYWdlIG9mIHRoZSBsZW5ndGggb2YgdGhlIHNob3J0ZXN0IHBhdGggYmV0d2VlbiBub2RlICppKiBhbmQgYWxsIG90aGVyIG5vZGVzIGluIHRoZSBuZXR3b3JrLiBJZiBhIG5vZGUgaGFzIGhpZ2ggY2xvc2VuZXNzIGNlbnRyYWxpdHksIGl0IG1lYW5zIHRoYXQgb24gYXZlcmFnZSwgaXQgdGFrZXMgZmV3IHN0ZXBzIHRvIHRyYXZlbCBmcm9tIHRoYXQgbm9kZSB0byBhbGwgb3RoZXIgbm9kZXMgaW4gdGhlIG5ldHdvcmsuIElmIGEgbm9kZSBoYXMgbG93IGNsb3NlbmVzcyBjZW50cmFsaXR5LCBpdCBtZWFucyB0aGF0IG9uIGF2ZXJhZ2UsIGl0IHRha2VzIG1vcmUgc3RlcHMgdG8gdHJhdmVsIGZyb20gdGhhdCBub2RlIHRvIGFsbCBvdGhlciBub2RlcyBpbiB0aGUgbmV0d29yay4KCkNsb3NlbmVzcyBjZW50cmFsaXR5IGlzIGNvbW1vbmx5IHZpZXdlZCBhcyBhbiBpbmRpY2F0b3Igb2YgdGhlICphY2Nlc3NpYmlsaXR5KiBvZiBhIG5vZGUgaW4gdGhlIG5ldHdvcmsgZnJvbSBhbGwgb3RoZXIgbG9jYXRpb25zIGluIHRoZSBuZXR3b3JrLiAKCiFbXShodHRwczovL3d3dy5yZWxpYW50c3Byb2plY3QuY29tL3dwLWNvbnRlbnQvdXBsb2Fkcy8yMDIwLzA2L3JlbGlhbnRzX2tleWNvbmNlcHRzLTA0LnBuZykKKlRoaXMgaXMgYSBmYW1vdXMgbmV0d29yayAoS3JhY2toYXJkdCdzIEtpdGUpIHRoYXQgbmljZWx5IGlsbHVzdHJhdGVzIHRoZSBkaWZmZXJlbmNlcyBiZXR3ZWVuIGRlZ3JlZSwgY2xvc2VuZXNzLCBhbmQgYmV0d2Vlbm5lc3MgY2VudHJhbGl0eS4qCgpgYGB7cn0KIyBjbG9zZW5lc3MgY2VudHJhbGl0aWVzIGZvciBkaXJlY3RlZCBuZXR3b3JrcywgaWdub3Jpbmcgd2VpZ2h0cyAgCmNsb3NlbmVzcyhncmFwaCA9IHNuZXQsIG5vcm1hbGl6ZWQgPSBULCBtb2RlID0gJ2FsbCcsIHdlaWdodHMgPSBOQSkgfD4gaGVhZCgpICMgYm90aCBpbi0gYW5kIG91dC0KY2xvc2VuZXNzKGdyYXBoID0gc25ldCwgbm9ybWFsaXplZCA9IFQsIG1vZGUgPSAnaW4nLCB3ZWlnaHRzID0gTkEpIHw+IGhlYWQoKSAjIG9ubHkgaW5jb21pbmcgZWRnZXMgY29uc2lkZXJlZCAKY2xvc2VuZXNzKGdyYXBoID0gc25ldCwgbm9ybWFsaXplZCA9IFQsIG1vZGUgPSAnb3V0Jywgd2VpZ2h0cyA9IE5BKSB8PiBoZWFkKCkgIyBvbmx5IG91dGdvaW5nIGVkZ2VzIGNvbnNpZGVyZWQKCiMgd2VpZ2h0cyBhcmUgY29uc2lkZXJlZCBieSBkZWZhdWx0IGlmIGdyYXBoIGhhcyBhIHdlaWdodCBhdHRyaWJ1dGUgCmNsb3NlbmVzcyhncmFwaCA9IHNuZXQsIG5vcm1hbGl6ZWQgPSBULCBtb2RlID0gJ2FsbCcpIHw+IGhlYWQoKQpgYGAKCk5vdGUgdGhhdCBjbG9zZW5lc3MgY2VudHJhbGl0eSBjYW4gb25seSBiZSBtZWFuaW5nZnVsbHkgY29tcHV0ZWQgZm9yIGNvbm5lY3RlZCBncmFwaHMgKHNvIHRoYXQgYSBwYXRoIGV4aXN0cyBiZXR3ZWVuIGFueSBwYWlyIG9mIG5vZGVzKS4gSWYgdGhlcmUgYXJlIGRpc3RpbmN0IG5ldHdvcmsgY29tcG9uZW50cywgdGhpcyBtZWFucyB0aGF0IGZvciBzb21lIHNldHMgb2Ygbm9kZSBwYWlycywgdGhlIHBhdGggYmV0d2VlbiB0aGVtIGRvZXMgbm90IGV4aXN0IGFuZCBjbG9zZW5lc3MgY2Fubm90IGJlIGNvbXB1dGVkLiBVc3VhbGx5LCBuZXR3b3JrIHNjaWVudGlzdHMgZm9jdXMgdGhlaXIgYW5hbHlzaXMgb24gdGhlIGxhcmdlc3QgY29ubmVjdGVkIGNvbXBvbmVudCBvZiB0aGUgbmV0d29yayBhbmQgaWdub3JlIHRoZSBzbWFsbGVyIGNvbm5lY3RlZCBjb21wb25lbnRzICh2aWV3ZWQgYXMgb3V0bGllcnMpLiAKCkl0IGlzIHR5cGljYWwgdG8gaGF2ZSBgbm9ybWFsaXplZCA9IFRgIHNvIHRoYXQgdGhlIHZhbHVlcyBhcmUgbm9ybWFsaXplZCB3aXRoIHJlc3BlY3QgdG8gdGhlIHNpemUgb2YgdGhlIG5ldHdvcmsuIEFzIHVzdWFsLCB5b3UgY2FuIHNwZWNpZnkgdGhlIGBtb2RlYCBhbmQgYHdlaWdodHNgIGFyZ3VtZW50cyBhY2NvcmRpbmdseSBpZiB5b3UgaGF2ZSBkaXJlY3RlZC93ZWlnaHRlZCBuZXR3b3JrcyB0byBnZXQgdGhlIGNvcnJlc3BvbmRpbmcgdmVyc2lvbnMgb2YgY2xvc2VuZXNzIGNlbnRyYWxpdHkgY29tcHV0ZWQuIEhvd2V2ZXIsIGNhdXRpb24gaXMgbmVlZGVkIGFzIHRoZSBpbnRlcnByZXRhdGlvbiBvZiBgd2VpZ2h0c2AgaW4gdGhpcyBjb250ZXh0IGlzIHRvIGludGVycHJldCB0aGVtIGFzICoqZGlzdGFuY2VzKiogcmF0aGVyIHRoYW4gKmNvbm5lY3Rpb24gc3RyZW5ndGhzKjogaGlnaGVyIHdlaWdodHMgPSBsb25nZXIgZGlzdGFuY2VzIChGcm9tIGBpZ3JhcGhgIG1hbnVhbDogIklmIHRoZSBncmFwaCBoYXMgYSB3ZWlnaHQgZWRnZSBhdHRyaWJ1dGUsIHRoZW4gdGhpcyBpcyB1c2VkIGJ5IGRlZmF1bHQuIFdlaWdodHMgYXJlIHVzZWQgZm9yIGNhbGN1bGF0aW5nIHdlaWdodGVkIHNob3J0ZXN0IHBhdGhzLCBzbyB0aGV5IGFyZSBpbnRlcnByZXRlZCBhcyBkaXN0YW5jZXMuIikuIEl0IGlzIGhpZ2hseSByZWNvbW1lbmRlZCB0byByZWFkIHRoZSBtYW51YWwgY2FyZWZ1bGx5IHRvIHVuZGVyc3RhbmQgdGhlIG1lYXN1cmVzIHRoYXQgYXJlIGJlaW5nIGNvbXB1dGVkLiAKCjxwIHN0eWxlPSJjb2xvcjpibHVlOyI+KlJlbGV2YW50IGZpbmRpbmcgaW4gcHN5Y2hvbG9neTogU2lldyAoMjAxOCkgc2hvd2VkIHRoYXQgdGhlIGNsb3NlbmVzcyBjZW50cmFsaXR5IG9mIHdvcmRzIGluIGFuIG9ydGhvZ3JhcGhpYyBzaW1pbGFyaXR5IG5ldHdvcmsgaW5mbHVlbmNlZCB3b3JkIG5hbWluZyBhbmQgdmlzdWFsIGxleGljYWwgZGVjaXNpb24gcGVyZm9ybWFuY2UgZGlmZmVyZW50bHkuIFdvcmRzIG9mIGhpZ2hlciBjbG9zZW5lc3MgY2VudHJhbGl0aWVzIHdlcmUgcmVzcG9uZGVkIHRvIG1vcmUgcXVpY2tseSBpbiBsZXhpY2FsIGRlY2lzaW9uLCBidXQgd2VyZSBuYW1lZCBtb3JlIHNsb3dseS4qPC9wPgoKIyMjIEJldHdlZW5uZXNzIENlbnRyYWxpdHkgCgpCZXR3ZWVubmVzcyBjZW50cmFsaXR5IGlzIGEgbWVhc3VyZSBvZiB0aGUgZGVncmVlIHRvIHdoaWNoIG5vZGVzIHN0YW5kIGluIGJldHdlZW4gZWFjaCBvdGhlci4gQSBub2RlIHdpdGggYSBoaWdoIGJldHdlZW5uZXNzIGNlbnRyYWxpdHkgaXMgYSBub2RlIHRoYXQgaXMgZnJlcXVlbnRseSBmb3VuZCBpbiB0aGUgc2hvcnQgcGF0aHMgb2Ygb3RoZXIgcGFpcnMgb2Ygbm9kZXMgaW4gdGhlIG5ldHdvcmsuIEluIGNvbnRyYXN0LCBhIG5vZGUgd2l0aCBhIGxvdyBiZXR3ZWVubmVzcyBjZW50cmFsaXR5IGlzIGEgbm9kZSB0aGF0IGlzIG5vdCB1c3VhbGx5IGZvdW5kIGluIHRoZSBzaG9ydCBwYXRocyBvZiBub2RlIHBhaXJzLiBCZXR3ZWVlbm5lc3MgY2FuIGJlIHZpZXdlZCBhcyBhbiBpbmRpY2F0b3IgaWYgd2hldGhlciBhIG5vZGUgcmVwcmVzZW50cyBhICJib3R0bGVuZWNrIiBpbiB0aGUgc3lzdGVtLiAKCmBgYHtyfQojIHVuZGlyZWN0ZWQsIHVud2VpZ2h0ZWQgbmV0d29yayAKYmV0d2Vlbm5lc3MoZ3JhcGggPSBwbmV0LCBub3JtYWxpemVkID0gVCwgd2VpZ2h0cyA9IE5BLCBkaXJlY3RlZCA9IEYpIHw+IGhlYWQoKQoKIyBkaXJlY3RlZCwgd2VpZ2h0ZWQgbmV0d29yayAKYmV0d2Vlbm5lc3MoZ3JhcGggPSBzbmV0LCBub3JtYWxpemVkID0gVCwgd2VpZ2h0cyA9IE5VTEwsIGRpcmVjdGVkID0gVCkgfD4gaGVhZCgpICMgdXNlIHdlaWdodCBhbmQgZGlyZWN0aW9uCmBgYAoKVGhlIHNhbWUgY29uc2lkZXJhdGlvbnMgKGFib3V0IGNvbm5lY3RlZCBncmFwaHMsIGFkZGl0aW9uYWwgYXJndW1lbnRzIGZvciB3ZWlnaHRlZCBhbmQgZGlyZWN0ZWQgZ3JhcGhzLCBub3JtYWxpemF0aW9uLCBpbnRlcnByZXRhdGlvbiBvZiB3ZWlnaHRzIGFzIGRpc3RhbmNlcykgZnJvbSB0aGUgY2xvc2VuZXNzIGNlbnRyYWxpdHkgc2VjdGlvbiBhcHBsaWVzIHRvIHRoaXMgc2VjdGlvbiBhcyB3ZWxsLiAKCiMjIyBQYWdlIFJhbmsgQ2VudHJhbGl0eQoKUGFnZVJhbmsgaXMgYSBjZW50cmFsaXR5IG1lYXN1cmUgZGV2ZWxvcGVkIGJ5IEdvb2dsZSB0byByYW5rIHdlYnBhZ2VzICh0aGUgaGlzdG9yaWMgcGFwZXIgZGVzY3JpYmluZyB0aGUgYWxnb3JpdGhtIGNhbiBiZSB2aWV3ZWQgW2hlcmVdKGh0dHA6Ly9pbmZvbGFiLnN0YW5mb3JkLmVkdS9+YmFja3J1Yi9nb29nbGUuaHRtbCkpLiBUaGUgZ2VuZXJhbCBpZGVhIGlzIHRoYXQgYSByYW5kb20gd2Fsa2VyIHdpbGwgdHJhdmVyc2UgdGhlIG5ldHdvcmsgc3BhY2UgYW5kIHRoZWlyIHBhdGhzIGFyZSBiaWFzZWQgYnkgdGhlIGxpbmsgY29ubmVjdGl2aXR5IHN0cnVjdHVyZSBvZiB0aGUgbmV0d29yay4gVGhlIHJhbmRvbSB3YWxrZXIgcmVzdGFydHMgdGhlIHdhbGsgYWZ0ZXIgc29tZSB0aW1lIChzaW11bGF0aW5nICJib3JlZG9tIiBvZiB0aGUgc3VyZmVyKS4gVGhlIG51bWJlciBvZiB2aXNpdHMgcmVjZWl2ZWQgYnkgYSBub2RlIHByb3ZpZGVzIGFuIGluZGljYXRvciBvZiBpdHMgaW1wb3J0YW5jZSBpbiB0aGUgbmV0d29yay4gSW50dWl0aXZlbHksIHdlIGV4cGVjdCB0aGF0IG5vZGVzIGhhdmUgYSBoaWdoIFBhZ2VSYW5rIGlmIHRoZXJlIGFyZSBtYW55IG5vZGVzIHRoYXQgcG9pbnQgdG8gaXQsIG9yIGlmIHRoZXJlIGFyZSBub2RlcyB0aGF0IHBvaW50IHRvIGl0IHRoYXQgdGhlbXNlbHZlcyBoYXZlIGEgaGlnaCBQYWdlUmFuay4gCgpgYGB7cn0KIyB1bmRpcmVjdGVkLCB1bndlaWdodGVkIG5ldHdvcmsgCnBhZ2VfcmFuayhncmFwaCA9IHBuZXQsIGRpcmVjdGVkID0gRiwgd2VpZ2h0cyA9IE5BKSR2ZWN0b3IgfD4gaGVhZCgpCgojIGRpcmVjdGVkLCB3ZWlnaHRlZCBuZXR3b3JrIApwYWdlX3JhbmsoZ3JhcGggPSBzbmV0LCBkaXJlY3RlZCA9IFQsIHdlaWdodHMgPSBOVUxMKSR2ZWN0b3IgfD4gaGVhZCgpCmBgYAoKVGhlIGB3ZWlnaHRzYCBhbmQgYGRpcmVjdGVkYCBhcmd1bWVudHMgY2FuIGJlIGFkanVzdGVkIGRlcGVuZGluZyBvbiB5b3VyIGdyYXBoIHR5cGUuIEl0IGlzIGltcG9ydGFudCB0byBub3RlIHRoYXQgdGhlIGludGVycHJldGF0aW9uIG9mIGVkZ2Ugd2VpZ2h0cyBoZXJlIGlzIHRoYXQgb2YgImNvbm5lY3Rpb24gc3RyZW5ndGgiIChmcm9tIGBpZ3JhcGhgIG1hbnVhbDogIlRoaXMgZnVuY3Rpb24gaW50ZXJwcmV0cyBlZGdlIHdlaWdodHMgYXMgY29ubmVjdGlvbiBzdHJlbmd0aHMuIEluIHRoZSByYW5kb20gc3VyZmVyIG1vZGVsLCBhbiBlZGdlIHdpdGggYSBsYXJnZXIgd2VpZ2h0IGlzIG1vcmUgbGlrZWx5IHRvIGJlIHNlbGVjdGVkIGJ5IHRoZSBzdXJmZXIuIikuIFRoaXMgaXMgZGlmZmVyZW50IGZyb20gdGhlICJkaXN0YW5jZSIgaW50ZXJwcmV0YXRpb24gb2YgZWRnZSB3ZWlnaHRzIGJ5IGNsb3NlbmVzcyBhbmQgYmV0d2Vlbm5lc3MuIAoKPHAgc3R5bGU9ImNvbG9yOmJsdWU7Ij4qUmVsZXZhbnQgZmluZGluZyBpbiBwc3ljaG9sb2d5OiBHcmlmZml0aHMgZXQgYWwuICgyMDA3KSBzaG93ZWQgdGhhdCBQYWdlIFJhbmsgY2VudHJhbGl0aWVzIG9mIHdvcmRzIGluIGEgd29yZCBhc3NvY2lhdGlvbiBuZXR3b3JrIHByb3ZpZGVkIGdvb2QgcHJlZGljdGlvbnMgZm9yIHRoZSB3b3JkcyBnZW5lcmF0ZWQgYnkgcGFydGljaXBhbnRzIGluIGEgbGV0dGVyIGZsdWVuY3kgdGFzay4qPC9wPgoKIyMgTWVzby1sZXZlbCAoY29tbXVuaXR5IHN0cnVjdHVyZSkKCkEgY29tbW9uIGZlYXR1cmUgb2YgbWFueSByZWFsLXdvcmxkIG5ldHdvcmtzIGlzIHRoYXQgdGhleSBoYXZlICoqY29tbXVuaXR5IHN0cnVjdHVyZSoqLiBOb2RlcyBhcmUgY29uc2lkZXJlZCB0byBiZSBwYXJ0IG9mIHRoZSBzYW1lIGNvbW11bml0eSBpZiB0aGUgZGVuc2l0eSBvZiBjb25uZWN0aW9ucyBhbW9uZyB0aG9zZSBub2RlcyBpcyByZWxhdGl2ZWx5IGhpZ2hlciB0aGFuIHRoZSBkZW5zaXR5IG9mIGNvbm5lY3Rpb25zIGJldHdlZW4gbm9kZXMgZnJvbSBkaWZmZXJlbnQgY29tbXVuaXRpZXMgKE5ld21hbiwgMjAwNikuCgoqKk1vZHVsYXJpdHksIFEqKiwgaXMgYSBtZWFzdXJlIG9mIHRoZSBkZW5zaXR5IG9mIGxpbmtzIGluc2lkZSBjb21tdW5pdGllcyBpbiByZWxhdGlvbiB0byB0aGUgZGVuc2l0eSBvZiBsaW5rcyBiZXR3ZWVuIGNvbW11bml0aWVzIChGb3J0dW5hdG8sIDIwMTApLiBOZXR3b3JrcyB3aXRoIGhpZ2hlciBRIGFyZSBzYWlkIHRvIHNob3cgc3Ryb25nIGV2aWRlbmNlIG9mIGNvbW11bml0eSBzdHJ1Y3R1cmUuIAoKIVtdKGltZy9rYXJhdGUtY29tbXVuaXRpZXMucG5nKQoKKkNvbW11bml0aWVzIGFyZSBkZXBpY3RlZCBpbiBkaWZmZXJlbnQgY29sb3JzIGZyb20gYW5vdGhlciBmYW1vdXMgbmV0d29yazogWmFjaGFyeSdzIEthcmF0ZSBDbHViIE5ldHdvcmsqCgoqKkhvdyBkbyBuZXR3b3JrIHNjaWVudGlzdHMgImZpbmQiIGNvbW11bml0aWVzIGluIG5ldHdvcmtzPyoqIAoKTWFueSBjb21tdW5pdHkgZGV0ZWN0aW9uIG1ldGhvZHMgaGF2ZSBiZWVuIGRldmVsb3BlZCBieSBuZXR3b3JrIHNjaWVudGlzdHMgdG8gZGV0ZWN0IGNvbW11bml0aWVzIGluIG5ldHdvcmtzLiBFYWNoIGRpZmZlcnMgaW4gdGhlaXIgaW1wbGVtZW50YXRpb24sIGFuZCByZWZsZWN0cyB0aGUgY3JlYXRvcidzIGltcGxpY2l0IGRlZmluaXRpb24gb2Ygd2hhdCBpcyBhIGNvbW11bml0eS4gSW4gdGhpcyB0dXRvcmlhbCB3ZSBnbyB0aHJvdWdoIG9ubHkgb25lIG9mIHRoZXNlIG1ldGhvZHMgKExvdXZhaW4pIHRvIGRlbW9uc3RyYXRlIGFuIGV4YW1wbGUgb2YgY29tbXVuaXR5IGRldGVjdGlvbi4KCklmIHlvdSBhcmUgaW50ZXJlc3RlZCB0byBsZWFybiBtb3JlIGFib3V0IGNvbW11bml0eSBkZXRlY3Rpb24sIGNoZWNrIG91dCBGb3J0dW5hdG8gKDIwMTApIHdobyBwcm92aWRlZCBhIGNvbXByZWhlbnNpdmUgY29tcGFyaXNvbiBvZiB2YXJpb3VzIGNvbW11bml0eSBkZXRlY3Rpb24gdGVjaG5pcXVlcy4gCgojIyMgTG91dmFpbiBtZXRob2QgKCJncmVlZHksIG1heGltaXphdGlvbiBtZXRob2QiKQoKVGhlIGNvcmUgaWRlYSBiZWhpbmQgdGhpcyBtZXRob2QgaXMgdGhhdCBjb21tdW5pdGllcyBhcmUgZXNzZW50aWFsbHkg4oCcbWVyZ2Vyc+KAnSBvZiBzbWFsbCBjb21tdW5pdGllcyAoQmxvbmRlbCBldCBhbC4sIDIwMDgpLCByZWZsZWN0aW5nIHRoZSBzZWxmLXNpbWlsYXIgbmF0dXJlIG9mIGNvbXBsZXggbmV0d29ya3MuCgoxLiBFYWNoIG5vZGUgaXMgYXNzaWduZWQgdG8gb25lIGNvbW11bml0eSBzdWNoIHRoYXQgdGhlcmUgYXJlIGFzIG1hbnkgY29tbXVuaXRpZXMgYXMgdGhlcmUgYXJlIG5vZGVzLiBUaGVuIHJlbW92ZSBub2RlICppKiBmcm9tIGl0cyBjb21tdW5pdHkgYW5kIHBsYWNpbmcgaXQgaW4gdGhlIGNvbW11bml0eSBvZiB0aGUgbmVpZ2hib3Igd2hpY2ggeWllbGRzIHRoZSBncmVhdGVzdCBnYWluIGluIG1vZHVsYXJpdHkuIAogIC0gcmVwZWF0IGZvciBhbGwgbm9kZXMgaW4gdGhlIG5ldHdvcmsKCjIuIEEgbmV3IG5ldHdvcmsgaXMgYnVpbHQgd2hlcmUgbm9kZXMgYXJlIHRoZSAqY29tbXVuaXRpZXMgZm91bmQgaW4gdGhlIHByZXZpb3VzIHBoYXNlKi4gUmVwZWF0IFN0ZXAgMS4gCiAgLSByZXBlYXQgU3RlcCAxIGFuZCAyIHVudGlsIGl0IGlzIG5vdCBwb3NzaWJsZSB0byBmdXJ0aGVyIGluY3JlYXNlIHRoZSB2YWx1ZSBvZiBRCgpgYGB7cn0Kc2V0LnNlZWQoOCkKCiMgcnVuIHRoZSBjb21tdW5pdHkgZGV0ZWN0aW9uIGFsZ29yaXRobSAKcmVzdWx0c19sb3V2YWluIDwtIGNsdXN0ZXJfbG91dmFpbihncmFwaCA9IHBuZXQpCgojIG92ZXJhbGwgcmVzdWx0cyAKbW9kdWxhcml0eShyZXN1bHRzX2xvdXZhaW4pCnNpemVzKHJlc3VsdHNfbG91dmFpbikKCiMgc3BlY2lmaWMgY29tbXVuaXR5IG1lbWJlcnNoaXAgZm9yIGVhY2ggbm9kZSAKY2JpbmQoCiAgcmVzdWx0c19sb3V2YWluJG5hbWVzLAogIHJlc3VsdHNfbG91dmFpbiRtZW1iZXJzaGlwCikgCmBgYAoKU2F2aW5nIHRoZSBjb21tdW5pdHkgZGV0ZWN0aW9uIHJlc3VsdHMgYXMgYSBgY29tbXVuaXRpZXNgIG9iamVjdCBlbmFibGVzIHRoZSB1c2Ugb2Ygc3BlY2lhbCBmdW5jdGlvbnMgbGlrZSBgbW9kdWxhcml0eSgpYCBhbmQgYHNpemVzKClgIHRvIG9idGFpbiB0aGUgbW9kdWxhcml0eSBvZiB0aGUgbmV0d29yayBhbmQgaXRzIGNvbW11bml0eSBzaXplcy4gSSBoYXZlIGFsc28gaW5jbHVkZWQgY29kZSB0aGF0IHNob3dzIGhvdyB0byBleHRyYWN0IHRoZSBjb21tdW5pdHkgbWVtYmVyc2hpcHMgb2YgYWxsIG5vZGVzIGluIHRoZSBuZXR3b3JrIGZvciBmdXJ0aGVyIGFuYWx5c2lzLiBUaGlzIGFwcGxpZXMgdG8gdGhlIG90aGVyIGNvbW11bml0eSBkZXRlY3Rpb24gYWxnb3JpdGhtcyBhcyB3ZWxsLiAKCjxwIHN0eWxlPSJjb2xvcjpibHVlOyI+KlJlbGV2YW50IGZpbmRpbmcgaW4gcHN5Y2hvbG9neTogU2lldyAoMjAxMykgZm91bmQgcm9idXN0IGNvbW11bml0eSBzdHJ1Y3R1cmUgaW4gdGhlIHBob25vbG9naWNhbCBuZXR3b3JrLCBhbmQgd29yZHMgaW4gdGhlIHNhbWUgY29tbXVuaXR5IHRlbmRlZCB0byBzaGFyZSBzaW1pbGFyIGxleGljYWwgYW5kIHBob25vbG9naWNhbCBwcm9wZXJ0aWVzLio8L3A+CgojIyBNYWNyby1sZXZlbCAobmV0d29yay1sZXZlbCkKCkluIHRoaXMgc2VjdGlvbiwgd2Ugd2lsbCByZXZpZXcgbmV0d29yayBzY2llbmNlIG1lYXN1cmVzIHRoYXQgZGVzY3JpYmUgdGhlIG92ZXJhbGwgb3IgZ2xvYmFsIHN0cnVjdHVyZSBvZiB0aGUgZW50aXJlIG5ldHdvcmsuIFlvdSBjYW4gdGhpbmsgb2YgdGhlc2UgbWVhc3VyZXMgYXMgcHJvdmlkaW5nIGEgImJpcmQncyBleWUgdmlldyIgb2YgeW91ciBuZXR3b3JrLCBhbmQgdGhleSBhcmUgdXNlZnVsIGZvciBjb21wYXJpbmcgZGlmZmVyZW50IG5ldHdvcmsgcmVwcmVzZW50YXRpb25zLiAKCiMjIyBBdmVyYWdlIFNob3J0ZXN0IFBhdGggTGVuZ3RoIAoKKipBdmVyYWdlIHNob3J0ZXN0IHBhdGggbGVuZ3RoKiogKEFTUEwpIHJlZmVycyB0byB0aGUgbWVhbiBvZiB0aGUgc2hvcnRlc3QgcG9zc2libGUgcGF0aCBiZXR3ZWVuIGFsbCBwb3NzaWJsZSBwYWlycyBvZiBub2RlcyBpbiB0aGUgbmV0d29yay4gKFRoaXMgbG9vc2VseSBjb3JyZXNwb25kcyB0byB0aGUgaWRlYSBvZiAic2l4IGRlZ3JlZXMgb2Ygc2VwYXJhdGlvbiIgaW4gc29jaWFsIG5ldHdvcmtzLikgIAoKIVtdKGh0dHBzOi8vZXh0ZXJuYWwtY29udGVudC5kdWNrZHVja2dvLmNvbS9pdS8/dT1odHRwcyUzQSUyRiUyRnRzZTIubW0uYmluZy5uZXQlMkZ0aCUzRmlkJTNET0lQLjdNNXBtRzR3NU5ucDYxMGkxajdyTFFIYUZ2JTI2cGlkJTNEQXBpJmY9MSkKCipFeGFtcGxlIGRlcGljdGluZyB0aGUgc2hvcnRlc3QgcGF0aCBiZXR3ZWVuIG5vZGVzIDI1IGFuZCAxNi4qCgpgYGB7cn0KIyB1bmRpcmVjdGVkLCB1bndlaWdodGVkIG5ldHdvcmsgCmF2ZXJhZ2UucGF0aC5sZW5ndGgoZ3JhcGggPSBwbmV0KSAKCiMgYW4gYWx0ZXJuYXRpdmUgZnVuY3Rpb24gLSBib3RoIGdpdmUgdGhlIHNhbWUgcmVzdWx0IAptZWFuX2Rpc3RhbmNlKGdyYXBoID0gcG5ldCkKCiMgZGlyZWN0ZWQsIHdlaWdodGVkIG5ldHdvcmsgCm1lYW5fZGlzdGFuY2UoZ3JhcGggPSBzbmV0LCB3ZWlnaHRzID0gTlVMTCwgZGlyZWN0ZWQgPSBUKSAjIGNvbnNpZGVyIHdlaWdodHMgYW5kIGRpcmVjdGlvbgptZWFuX2Rpc3RhbmNlKGdyYXBoID0gc25ldCwgd2VpZ2h0cyA9IE5VTEwsIGRpcmVjdGVkID0gRikgIyBpZ25vcmUgZGlyZWN0aW9uIAptZWFuX2Rpc3RhbmNlKGdyYXBoID0gc25ldCwgd2VpZ2h0cyA9IE5BLCBkaXJlY3RlZCA9IFQpICMgaWdub3JlIHdlaWdodHMgCmBgYAoKPHAgc3R5bGU9ImNvbG9yOmJsdWU7Ij4qUmVsZXZhbnQgZmluZGluZyBpbiBwc3ljaG9sb2d5OiBTaWV3ICgyMDE4KSBzaG93ZWQgdGhhdCBjb25jZXB0IG5ldHdvcmtzIChjb25zdHJ1Y3RlZCBmcm9tIGNvbmNlcHQgbWFwcyBnZW5lcmF0ZWQgYnkgc3R1ZGVudHMpIHdpdGggbGFyZ2VyIGF2ZXJhZ2Ugc2hvcnRlc3QgcGF0aCBsZW5ndGhzIHdlcmUgYXNzb2NpYXRlZCB3aXRoIGhpZ2hlciBxdWl6IHNjb3JlcywgYWZ0ZXIgY29udHJvbGxpbmcgZm9yIG5ldHdvcmsgc2l6ZS4qPC9wPgoKIyMjIEdsb2JhbCBDbHVzdGVyaW5nIENvZWZmaWNpZW50ICAKCioqR2xvYmFsIGNsdXN0ZXJpbmcgY29lZmZpY2llbnQqKiByZWZlcnMgdG8gdGhlIG51bWJlciBvZiBjbG9zZWQgdHJpYW5nbGVzIGluIHRoZSBuZXR3b3JrIHJlbGF0aXZlIHRvIHRoZSBudW1iZXIgb2YgcG9zc2libGUgdHJpYW5nbGVzLiBJdCBpcyBhIG1lYXN1cmUgb2Ygb3ZlcmFsbCBsZXZlbCBvZiAqbG9jYWwqIGNvbm5lY3Rpdml0eSBhbW9uZyBub2RlcyBpbiB0aGUgbmV0d29yay4gCgpBIHNpbXBsZSB3YXkgb2YgdGhpbmtpbmcgYWJvdXQgdGhpcyBjb25jZXB0IGlzIHRoYXQgaXQgaXMgbWVhc3VyaW5nIHRoZSBwcm9iYWJpbGl0eSB0aGF0IGVhY2ggcGFpciBvZiAiZnJpZW5kcyIgb2YgYSBnaXZlbiBub2RlIGFyZSBhbHNvIGZyaWVuZHMgd2l0aCBlYWNoIG90aGVyLgoKYGBge3J9CnRyYW5zaXRpdml0eShncmFwaCA9IHBuZXQsIHR5cGUgPSAnZ2xvYmFsJykKdHJhbnNpdGl2aXR5KGdyYXBoID0gc25ldCwgdHlwZSA9ICdnbG9iYWwnKQpgYGAKCiMjIyBTbWFsbCBXb3JsZCBJbmRleCAKClRoZSB0ZXJtICJzbWFsbCB3b3JsZCIgaGFzIGEgc3BlY2lmaWMgbWVhbmluZyBpbiBuZXR3b3JrIHNjaWVuY2UgYXMgY29tcGFyZWQgdG8gdGhlIGxheXBlcnNvbidzLiBBIG5ldHdvcmsgaXMgY29uc2lkZXJlZCB0byBoYXZlIHNtYWxsIHdvcmxkIGNoYXJhY3RlcmlzdGljcyBpZiAoaSkgaXRzIEFTUEwgaXMgKnNob3J0ZXIqIHRoYW4gdGhhdCBvZiBhIHJhbmRvbWx5IGdlbmVyYXRlZCBuZXR3b3JrIHdpdGggdGhlIHNhbWUgbnVtYmVyIG9mIG5vZGVzIGFuZCBlZGdlcywgYW5kIChpaSkgaXRzIGdsb2JhbCBDIGlzICpsYXJnZXIqIHRoYW4gdGhhdCBvZiBhIHJhbmRvbWx5IGdlbmVyYXRlZCBuZXR3b3JrIHdpdGggdGhlIHNhbWUgbnVtYmVyIG9mIG5vZGVzIGFuZCBlZGdlcy4gVGhlcmUgYXJlIHZhcmlvdXMgd2F5cyB0byBjb21wdXRlIGEgdmFsdWUgdGhhdCBxdWFudGlmaWVzIHRoZSAic21hbGwgd29ybGRuZXNzIiBvZiBhIG5ldHdvcmssIGFsdGhvdWdoIHdlIGRvIG5vdCBjb3ZlciB0aGVtIGhlcmUgKHNlZSBIdW1waHJpZXMgYW5kIEd1cm5leSwgMjAwOCwgZm9yIGFuIGV4YW1wbGUsIGFuZCBOZWFsLCAyMDE3LCBmb3IgYSBjb21wYXJpc29uIG9mIGRpZmZlcmVudCBtZXRob2RzKS4KClRoZSBtYWluIHRha2UgaG9tZSBtZXNzYWdlIGlzIHRoYXQgYSBzbWFsbCB3b3JsZCBuZXR3b3JrIGhhcyBoaWdoIGxldmVscyBvZiBsb2NhbCBjbHVzdGVyaW5nIChub2RlcyB3aG9zZSBuZWlnaGJvcnMgYXJlIGFsc28gbmVpZ2hib3JzIG9mIGVhY2ggb3RoZXIpLCBidXQgdGhlcmUgYWxzbyBleGlzdHMgYSBudW1iZXIgb2Ygc2hvcnRjdXRzIHRoYXQgZHJhc3RpY2FsbHkgcmVkdWNlcyB0aGUgb3ZlcmFsbCBkaXN0YW5jZXMvcGF0aCBsZW5ndGhzIGJldHdlZW4gbm9kZXMuIFNlZSBiZWxvdyBmb3IgYW4gaWxsdXN0cmF0aW9uIG9mIHRoaXMgaWRlYS4gCgohW10oaW1nL3NtYWxsLXdvcmxkLmdpZikKCiMjIyBOZXR3b3JrIERlbnNpdHkKCioqTmV0d29yayBkZW5zaXR5KiogcmVmZXJzIHRvIHRoZSByYXRpbyBvZiB0aGUgbnVtYmVyIG9mIChleGlzdGluZykgZWRnZXMgYW5kIHRoZSBudW1iZXIgb2YgcG9zc2libGUgZWRnZXMgYW1vbmcgbm9kZXMgaW4gdGhlIG5ldHdvcmsuIAoKIVtdKGltZy9kZW5zaXR5LnBuZykKCipTaW1wbGUgZXhhbXBsZSBvZiBuZXR3b3JrcyB3aXRoIGxvd2VyIGFuZCBoaWdoZXIgbmV0d29yayBkZW5zaXRpZXMuKgoKYGBge3J9CmdyYXBoLmRlbnNpdHkoZ3JhcGggPSBwbmV0KQpncmFwaC5kZW5zaXR5KGdyYXBoID0gc25ldCkKYGBgCgojIyMgTmV0d29yayBEaWFtZXRlciAKCioqTmV0d29yayBkaWFtZXRlcioqIHJlZmVycyB0byBsZW5ndGggb2YgdGhlIGxvbmdlc3Qgc2hvcnRlc3QgcGF0aCBiZXR3ZWVuIG5vZGVzIGluIHRoZSBuZXR3b3JrLiBJbnN0ZWFkIG9mIGdldHRpbmcgdGhlIG1lYW4gb2YgYWxsIHRoZSBzaG9ydGVzdCBwYXRocyBhcyB5b3UgZGlkIGluIEFTUEwsIHdoYXQgaXMgdGhlICptYXhpbXVtKiBsZW5ndGggb2YgdGhvc2Ugc2hvcnQgcGF0aHM/IAoKIVtdKGltZy9kaWFtZXRlci5wbmcpCgoqU2ltcGxlIGV4YW1wbGUgb2YgbmV0d29ya3Mgd2l0aCBoaWdoZXIgYW5kIGxvd2VyIG5ldHdvcmsgZGlhbWV0ZXJzKgoKYGBge3J9CiMgdW5kaXJlY3RlZCwgdW53ZWlnaHRlZCBncmFwaCAKZGlhbWV0ZXIoZ3JhcGggPSBwbmV0KQoKIyBkaXJlY3RlZCwgd2VpZ2h0ZWQgZ3JhcGggCmRpYW1ldGVyKGdyYXBoID0gc25ldCwgZGlyZWN0ZWQgPSBULCB3ZWlnaHRzID0gTlVMTCkgIyBjb25zaWRlciB3ZWlnaHRzIGFuZCBkaXJlY3Rpb24KZGlhbWV0ZXIoZ3JhcGggPSBzbmV0LCBkaXJlY3RlZCA9IEYsIHdlaWdodHMgPSBOVUxMKSAjIGlnbm9yZSBkaXJlY3Rpb24gCmRpYW1ldGVyKGdyYXBoID0gc25ldCwgZGlyZWN0ZWQgPSBULCB3ZWlnaHRzID0gTkEpICMgaWdub3JlIHdlaWdodHMgCmBgYAoKIyBBZGRpdGlvbmFsIFJlc291cmNlcyAKCk9nbnlhbm92YSwgSy4gKDIwMjEpIE5ldHdvcmsgdmlzdWFsaXphdGlvbiB3aXRoIFIuIFJldHJpZXZlZCBmcm9tIHd3dy5rYXRldG8ubmV0L25ldHdvcmstdmlzdWFsaXphdGlvbi4gaHR0cHM6Ly9rYXRldG8ubmV0L25ldHdvcmstdmlzdWFsaXphdGlvbgoKVGhlIG9mZmljaWFsIGBpZ3JhcGhgIG1hbnVhbCAodi4xLjMuNCkuIGh0dHBzOi8vaWdyYXBoLm9yZy9yL2RvYy8gCgpHZXBoaTogQSBtdWx0aS1wbGF0Zm9ybSwgZnJlZSB0byBkb3dubG9hZCBHVUkgYXBwIGZvciBuZXR3b3JrIGFuYWx5c2lzIGFuZCB2aXN1YWxpemF0aW9uLiBodHRwczovL2dlcGhpLm9yZy8KCiMgUmVmZXJlbmNlcyAKCkJhcnJhdCwgQS4sIEJhcnRow6lsZW15LCBNLiwgUGFzdG9yLVNhdG9ycmFzLCBSLiwgJiBWZXNwaWduYW5pLCBBLiAoMjAwNCkuIFRoZSBhcmNoaXRlY3R1cmUgb2YgY29tcGxleCB3ZWlnaHRlZCBuZXR3b3Jrcy4gUHJvY2VlZGluZ3Mgb2YgdGhlIE5hdGlvbmFsIEFjYWRlbXkgb2YgU2NpZW5jZXMsIDEwMSgxMSksIDM3NDfigJMzNzUyLiBodHRwczovL2RvaS5vcmcvMTAuMTA3My9wbmFzLjA0MDAwODcxMDEKCkJsb25kZWwsIFYuIEQuLCBHdWlsbGF1bWUsIEouIEwuLCBMYW1iaW90dGUsIFIuLCAmIExlZmVidnJlLCBFLiAoMjAwOCkuIEZhc3QgdW5mb2xkaW5nIG9mIGNvbW11bml0aWVzIGluIGxhcmdlIG5ldHdvcmtzLiBKb3VybmFsIG9mIFN0YXRpc3RpY2FsIE1lY2hhbmljczogVGhlb3J5IGFuZCBFeHBlcmltZW50LCAyMDA4KDEwKSwgUDEwMDA4LgoKQ2hhbiwgSy4gWS4sICYgVml0ZXZpdGNoLCBNLiBTLiAoMjAwOSkuIFRoZSBpbmZsdWVuY2Ugb2YgdGhlIHBob25vbG9naWNhbCBuZWlnaGJvcmhvb2QgY2x1c3RlcmluZyBjb2VmZmljaWVudCBvbiBzcG9rZW4gd29yZCByZWNvZ25pdGlvbi4gSm91cm5hbCBvZiBFeHBlcmltZW50YWwgUHN5Y2hvbG9neTogSHVtYW4gUGVyY2VwdGlvbiBhbmQgUGVyZm9ybWFuY2UsIDM1KDYpLCAxOTM04oCTMTk0OS4gaHR0cHM6Ly9kb2kub3JnLzEwLjEwMzcvYTAwMTY5MDIKCkRlIERleW5lLCBTLiwgTmF2YXJybywgRC4gSi4sIFBlcmZvcnMsIEEuLCBCcnlzYmFlcnQsIE0uLCAmIFN0b3JtcywgRy4gKDIwMTkpLiBUaGUg4oCcU21hbGwgV29ybGQgb2YgV29yZHPigJ0gRW5nbGlzaCB3b3JkIGFzc29jaWF0aW9uIG5vcm1zIGZvciBvdmVyIDEyLDAwMCBjdWUgd29yZHMuIEJlaGF2aW9yIFJlc2VhcmNoIE1ldGhvZHMsIDUxLCA5ODfigJMxMDA2LgoKRm9ydHVuYXRvLCBTLiAoMjAxMCkuIENvbW11bml0eSBkZXRlY3Rpb24gaW4gZ3JhcGhzLiBQaHlzaWNzIFJlcG9ydHMsIDQ4NigzLTUpLCA3NS0xNzQuCgpIdW1waHJpZXMsIE0uIEQuLCAmIEd1cm5leSwgSy4gKDIwMDgpLiBOZXR3b3JrIOKAmHNtYWxsLXdvcmxkLW5lc3PigJk6IEEgcXVhbnRpdGF0aXZlIG1ldGhvZCBmb3IgZGV0ZXJtaW5pbmcgY2Fub25pY2FsIG5ldHdvcmsgZXF1aXZhbGVuY2UuIFBsb1MgT25lLCAzKDQpLgoKR3JpZmZpdGhzLCBULiBMLiwgU3RleXZlcnMsIE0uLCAmIEZpcmwsIEEuICgyMDA3KS4gR29vZ2xlIGFuZCB0aGUgTWluZDogUHJlZGljdGluZyBGbHVlbmN5IFdpdGggUGFnZVJhbmsuIFBzeWNob2xvZ2ljYWwgU2NpZW5jZSwgMTgoMTIpLCAxMDY54oCTMTA3Ni4gaHR0cHM6Ly9kb2kub3JnLzEwLjExMTEvai4xNDY3LTkyODAuMjAwNy4wMjAyNy54CgpMdWNlLCBQLiBBLiwgJiBQaXNvbmksIEQuIEIuICgxOTk4KS4gUmVjb2duaXppbmcgc3Bva2VuIHdvcmRzOiBUaGUgTmVpZ2hib3Job29kIEFjdGl2YXRpb24gTW9kZWwuIEVhciBhbmQgSGVhcmluZywgMTkoMSksIDHigJMzNi4KCk5lYWwsIFouIFAuICgyMDE3KS4gSG93IHNtYWxsIGlzIGl0PyBDb21wYXJpbmcgaW5kaWNlcyBvZiBzbWFsbCB3b3JsZGxpbmVzcy4gTmV0d29yayBTY2llbmNlLCA1KDEpLCAzMOKAkzQ0LiBodHRwczovL2RvaS5vcmcvMTAuMTAxNy9ud3MuMjAxNy41CgpOZXdtYW4sIE0uIEUuICgyMDA2KS4gTW9kdWxhcml0eSBhbmQgY29tbXVuaXR5IHN0cnVjdHVyZSBpbiBuZXR3b3Jrcy4gUHJvY2VlZGluZ3Mgb2YgdGhlIE5hdGlvbmFsIEFjYWRlbXkgb2YgU2NpZW5jZXMsIDEwMygyMyksIDg1NzctODU4Mi4gICAKClNpZXcsIEMuIFMuIFEuICgyMDEzKS4gQ29tbXVuaXR5IHN0cnVjdHVyZSBpbiB0aGUgcGhvbm9sb2dpY2FsIG5ldHdvcmsuIEZyb250aWVycyBpbiBQc3ljaG9sb2d5LCA0LCA1NTMuCgpTaWV3LCBDLiBTLiBRLiAoMjAxOCkuIFRoZSBvcnRob2dyYXBoaWMgc2ltaWxhcml0eSBzdHJ1Y3R1cmUgb2YgRW5nbGlzaCB3b3JkczogSW5zaWdodHMgZnJvbSBuZXR3b3JrIHNjaWVuY2UuIEFwcGxpZWQgTmV0d29yayBTY2llbmNlLCAzKDEpLCAxMy4KClNpZXcsIEMuIFMuIFEuICgyMDE4KS4gVXNpbmcgbmV0d29yayBzY2llbmNlIHRvIGFuYWx5emUgY29uY2VwdCBtYXBzIG9mIHBzeWNob2xvZ3kgdW5kZXJncmFkdWF0ZXMuIEFwcGxpZWQgQ29nbml0aXZlIFBzeWNob2xvZ3kuCgpWaXRldml0Y2gsIE0uIFMuICgyMDA4KS4gV2hhdCBjYW4gZ3JhcGggdGhlb3J5IHRlbGwgdXMgYWJvdXQgd29yZCBsZWFybmluZyBhbmQgbGV4aWNhbCByZXRyaWV2YWw/IEpvdXJuYWwgb2YgU3BlZWNoLCBMYW5ndWFnZSwgYW5kIEhlYXJpbmcgUmVzZWFyY2gsIDUxKDIpLCA0MDjigJM0MjIuIGh0dHBzOi8vZG9pLm9yZy8xMC4xMDQ0LzEwOTItNDM4OCgyMDA4LzAzMCkK
Copyright © 2023 CSQ Siew. All rights reserved.

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