This allows places with different rules to coexist” — Eugen Rochko, Learning from Twitter’s Mistake
Folklore was self-generating” — China Melville, Kraken

Mastodon is the latest offspring of a complex and fascinating lineage of free and decentralized social networks. While Facebook, Google and Twitter attempted to build large commercial empire on the trading of social interaction, free software projects did not remain inactive: GNU Social, Identi.ca, Diaspora, Friendica.

Mastodon was only a small newcomer to this connected galaxy of counter-social networks: it is actually based on a common standard defined Gnu Social, OStatus — so that GNU Social accounts can communicate with Mastodon accounts. And the success story came, unsuspected and unprecedented: in less than a weeks, whole communities have shifted from Twitter to Mastodon.

Mastodon is not a social network but, rather, a network of social networks. Anyone can set up and instance (even though that’s technically challenging and costly), host the data of the people joining in and define specific rules (for instance regarding moderation) ; all the instances are linked into a “federation” (and continuously exchange texts and interaction). While this infrastructure designed by Eugen Rothko and a team of volunteer developers is not revolutionary per se it has been elegantly refined (with, for instance, a much friendlier interface than the vanilla Twitter and much more diverse privacy controls).

Mastodon claims to solve a wide array of structural shortcomings within social networks: a structural inability to define the proper balance between censorship and the dissemination of hate speech and harassement, a massive exploitation of private interactions, an interface more and more modelled around advertising returns… Consequently, noboby “owns” Mastodon as a whole (and has the ability to shift it to a commercial model) and each instance can set up their own code, that can meet the needs of specific people and communities.

This promise is the starting point of my exploratory research. I hypothesize that Mastodon is intrinsically build to favor the gathering of communities. The technical design of instances immediately raises gouvernance issues (what has to be regulated? and how to regulate it?) which in turn favors the expression, if not the genesis of a specific “culture” — made perceptible from the outside by the use of shared languages and references. This cultural genesis is tacitly acknowledged by Mastodon’s initiator, Eugen Rochko:

Different instances, owned by different entities, will have different rules and moderation policies. This gives the power to shape smaller, independent, yet integrated communities back to the people.

And it is not a new process on the web. Large scale projects, such as Wikipedia or Debian, had a similar tendency to develop a strong “sense of community”, merely from the necessity to cope with common problems and to solve them in common.

Although this research claims to be of an “exploratory” nature, it may seems a bit paradoxical to study a network that has only gone mainstream for several days and remains by all accounts at a very early stage of development. A new form of scientific writing makes this initiative a bit more appropriate: the code notebook.

This is not a definite article but the translation of a work in progress. All the text is structured around “code blocks” in R that can be easily changed and easily challenged: altering one variable or one function may fully transform the dataset or a graph and generates new insight. The original code, data and rmd files are available on Github and can be run again (or tweaked, for that matter) on R studio.

I’m likely to subdivide this research into three episodes (but even that can change). This first part mostly deals with the “interactions” within the networks (do the mentions reflect some preferences toward the user of a same instance?). The second part will look at the cultural genesis issue through the lenses of text mining (are the textual content of toots somewhat related accross an instance? to what extent does an instance specialize on some issues or thematics?). The final part is still fuzzy but will at least draw some broader analysis (by comparing Mastodon with precedents in other free web communities or contextualizing its part within the wider platform cooperativism moment).

A friendly API

A key feature of Mastodon did not fully get the appraisal it deserves: the API. While being instrumental to the development of Twitter Studies, Twitter’s API has become more and more convoluted, as the social network focused its economic development plan on the monetization of users’ data.

In its current form, Mastodon is fully much more friendly to the casual text & data miner. Even though the social network is in its prime, there’s already dedicated libraries to deal with the API in Ruby and Python. Since the 1.1.1 update, there is no need to login to access to the public timeline: everyone can set up an API key in a few seconds (that’s the purpose of the create_mastodon_app.py script, to be run just once).

The public timeline do not register all the publications on Mastodon: every toot with privacy setting, every deleted toot and, likely, every instance not connected to mastodon.social do not show up. Based on the toot id, it seems that my corpora represents 20.78% of all toots (as stated earlier that value is generated by R and can change depending on the available data). This is far from exhaustive but still enough to deduce some interesting dynamics.

Now there’s a function in particular that made me fall in love with this API: “pace”. A recurring problem I had when analyzing API data, is that it is necessary to severily restrain the rate of automatic extraction. While limiting the number of calls to the API to relieve the server is perfectly understandable, it frequently ends up in adding unnecessary seconds of “sleep” to the code (for instance, through time.sleep()). With pace, all the wait is optimized from both side: the server slows down whenever there is too much pressure and accelerates otherwise. It is not necessary to devise devious tactics in order to take the max out the API calls while avoiding getting ban.

mastodon = Mastodon(client_id = 'pytooter_clientcred.txt', ratelimit_method='pace')

There’s little to say about the two python scripts used to retrieve the data (mastodon_retrieve_API.py) and to put the more interesting pieces of information in a csv (mastodon_extract_json.py). The first script define a loop of 100 calls with a starting id (which has to be changed at the end of each run, by using the last id printed on the terminal, or the name of the last file in the corpus directory). It takes about five minutes to make the 100 calls — even though that may change depending on “rate”. Each calls retrieve 20 “toots” — the Mastodon counterpart of tweets. I have also included a time.sleep of 10 seconds with an exception: from time to time a general API error pops up on the server side (which has become much less frequent with the 1.1.1 update).

for passing in range(0,100):
    try:
        print("getting the mastodon timeline json")
        timeline_mastodon = mastodon.timeline_public(max_id = last_id)
        json_mastodon = json.dumps(timeline_mastodon)
        last_id = timeline_mastodon[len(timeline_mastodon)-1]["id"]
        print("last stop at toot id " + str(last_id))
        json_title = "mastodon_corpus/" + str(last_id) + ".json"
        json_save_mastodon = open(json_title, 'w')
        json_save_mastodon.write(json_mastodon)
    except:
        print("API error, waiting a bit to see what happens")
        time.sleep(10)

The second script extract and clean the json files in a somewhat ugly manneer — that’s not the code I’m the most proud of, but I was looking forward to the proper data analysis.

Before that, just a small caveat on the issue of “personal data”. While this is the public timeline of Mastodon and, with a little time, anyone can retrieve the similar information through the API, I’m still a little uncertain of what is the proper ethics way of dealing with social interactions of “real people” (all the more as concern for privacy is one of the leading concern of Mastodon). For the time being, the dataset available on this github directory only keeps the information actually used in this code notebook (which excludes the textual content of each publication, the “toots”). I have also replaced the personal name of each account by their id: while the data is not anonyminized, it will be necessary to call the API once more to get any relevant personal information.

First steps in R

And, data analysis, here we come ! Unexpectedly, our first steps aims at loading a couple of libraries and cleaning a bit the csv file delivered by the python script.

library(tidyverse)
library(lubridate)
library(ggplot2)
library(stringr)
mastodon <- tbl_df(read_csv2("mastodon_interactions.csv"))

read_csv2 tries to “guess” the original format of the values (whether it is a character, an integer…). The process is efficient, but not full proof…

For instance the two date values (publication of the toot, and creation of the Mastonaut account) are encoded as characters. Fortunately, they are also in a standard format: ymd_hms (from lubridate) can easily parse them. We also create a new column created_at_hour, that only keeps the hours for each day (and remove the minutes and seconds). And by the way we ensure that all the dates are registered after “2017-04-06 05:00:00” (there are some outliers, probably due to the “boost” of older toots).

mastodon <- mastodon %>% 
  mutate(created_at_hour = str_sub(created_at, 0, -12)) %>% 
  mutate(created_at_hour = ymd_h(created_at_hour)) %>%
  mutate(created_at = ymd_hms(created_at)) %>% 
  filter(created_at > ymd_hms("2017-04-06 05:00:00"))

Next, we have to “separate” the instance chosen by the user from his name. The *@* provides an unambiguous way to split the strings and redefine two new columns: account_instance and account_name. Since we operate from a mastodon.social timeline, the mastodon.social instance is implied: we replace accordingly the non values generated on account_instance.

mastodon <- mastodon %>% 
  separate(account, c("account_name", "account_instance"), sep="@") %>% 
  mutate(account_instance = ifelse(is.na(account_instance), "mastodon.social", account_instance))

A first look to our dataframe suggests that all seems in order :

select(mastodon, toot_id, created_at, created_at_hour, account_name)

An European-centered network
Insights from the day/night GMT contrast

So we have a middle-sized data frame of 56627 toots that goes from 2017-04-06 05:03:01 to 2017-04-08 21:35:04 which is ready for some simple temporal dataviz. Thanks to our created_at_hour column we are able to display the evolution of the global participation on the public timeline during the period. The day/night contrast is noticeably strong. Since the time is expressed is GMT, the demography of the Mastonauts is likely centered on European countries. My personal experience relates to this hypothesis: so far the American accounts I follow on Twitter have not migrated yet…

mastodon %>% 
  filter(created_at_hour != created_at_hour[which.min(created_at_hour)]) %>%
  filter(created_at_hour != created_at_hour[which.max(created_at_hour)]) %>%
  group_by(created_at_hour) %>%
  summarise(n_hour=n()) %>%
  ggplot(aes(created_at_hour, n_hour)) + 
  geom_line(color="red", size=1) +
  ylim(0,1500) +
  labs(x="Hour", y="Number of toots", title="Participation within all instances of Mastodon", caption="Source : Public timeline of mastodon.social")

Yet, Mastodon is not one big centralized network but a “fedaration” of individualized instances. Our variable main_instances get the name of the most active instances. Here I have selected the top 6 instances () minus mastodon.social. You can easily change theses settings by chosing a different threshold on top_n or removing the filter(account_instance =! “mastodon.social”) %>% if you want to keep mastodon.social.

main_instances <- mastodon %>%
  group_by(account_instance) %>%
  summarise(toots_instance = n()) %>%
  filter(account_instance != "mastodon.social") %>%
  top_n(6) %>%
  arrange(desc(toots_instance))
main_instances

Now our graph includes the following instances : mastodon.xyz, octodon.social, mastodon.cloud, witches.town, social.tchncs.de, icosahedron.website. Even though the graph lines are rather intertwines with our default settings, the day/night contrast tends clearly to vary accross the instances. That may suggest that some instances are more “localized” or more “international” than others.

mastodon %>%
  filter(created_at_hour != created_at_hour[which.min(created_at_hour)]) %>%
  filter(created_at_hour != created_at_hour[which.max(created_at_hour)]) %>%
  filter(account_instance %in% main_instances$account_instance) %>%
  group_by(created_at_hour, account_instance) %>% 
  summarise(n_hour=n()) %>%
  ggplot(aes(created_at_hour, n_hour, color=account_instance)) + 
  geom_line(size=1) +
  theme(legend.position="bottom") +
  labs(x="Hour", y="Number of toots", color="Instance", title="Participation by instances", caption="Source : Public timeline of mastodon.social")

To have a quick idea of how much instances are affected by the GMT night-day shift, we can check the enthropy value. Time series decomposition would be more relevant but since we still have little data avaiable, a rough approximation of how much the tendency deviates from uniform distribution shall do the trick. The “do” function stated below is a precious addition to R that allows to apply any function to each group of data.

library(entropy)
mastodon_entropy <- mastodon %>%
  filter(created_at_hour != created_at_hour[which.min(created_at_hour)]) %>%
  filter(created_at_hour != created_at_hour[which.max(created_at_hour)]) %>%
  filter(account_instance %in% main_instances$account_instance) %>%
  group_by(created_at_hour, account_instance) %>% 
  summarise(n_hour=n()) %>%
  ungroup() %>%
  group_by(account_instance) %>%
  do(entropy_value = entropy(.$n_hour, method="MM")) %>%
  unnest(entropy_value)
ggplot(mastodon_entropy, aes(account_instance, entropy_value, size=1, color=account_instance)) + 
  geom_point() +
  ylim(3.9, 4.2) +
  guides(size=FALSE, color=FALSE) +
  labs(x="Instance", y="Entropy value", color="Instance", title="Entropy value accross the hours by instances", caption="Source : Public timeline of mastodon.social")

Here the two lowest value (that is that deviates the most from the uniform distribution) are attained by two instances created in Germany, icosahedron.web and social.tchncs.de, whereas mastodon.cloud and octodon.social (which claims to “federate everywhere”) seems more likely to attract non-european users.

How do instances relates to each other?
A network of notifications.

Now it’s time for the tricky part: trying to map the network as… a network. As typical of any social network, toots are not isolated publications, but are “connected” through a wide array of interactions. And, less typically, theses interactions are part of wider interrelation between the “instances” and can help us to check their inner “community coherences”: the less an instance is linked to other instances, the more likely it seems to be community-centric.

The “mentions” are the most interesting data immediately available to get build a network model. Yet, they are not presented in a custom format, but as “lists”. A quick inspection on our dataframe confirms that we have to deal with a comma-separated combination of an unknown number of accounts.

library(stringr)
mastodon %>%
  select(toot_mentions_account) %>%
  filter(str_detect(toot_mentions_account, ","))

Before we can get any further, we have to “unnest” our dataframe so that each row maps a specific mention (all the related metadata being consequently repeated). The id and the name of the accounts targeted by the mentions are “split” into a list (with strstplit) and the whole dataset is “unnested” according to the list of mentions. Then we “separate” the account and instances of each mentions (as we did on the names of the account earlier on).

mastodon_replies <- mastodon %>% 
  select(toot_mentions_account, created_at, account_name, account_instance) %>% 
  filter(toot_mentions_account != "None") %>% 
  mutate(toot_mentions_account = strsplit(toot_mentions_account, ", ")) %>% 
  unnest(toot_mentions_account)
mastodon_replies <- mastodon_replies %>%
  separate(toot_mentions_account, c("mention_name", "mention_instance"), sep="@") %>% 
  mutate(mention_instance = ifelse(is.na(mention_instance), "mastodon.social", mention_instance))
mastodon_replies

Everything is now maped around the “mention” unit : each row is a mention and when their is several mentions, the account name, instances, id, etc. are repeated accordingly.

Since there is a lot of instances (277 on the account side), we only keeps the main one. Here our calculation is a bit more complicated than when we tried to define main_instances: we try to get the global count of instances on both side of the account-mention relationship, so that we have to merge account and mention instances into an unified “instance” variable. With a network visualisation a higher threshold is acceptable : we are settling for 20. “mastodon.social” is not filetered either, as it will be less overwhelming.

main_reply_instances <- data_frame(c(mastodon_replies$account_instance, mastodon_replies$mention_instance)) %>%
  setNames(., c("instance")) %>%
  group_by(instance) %>%
  summarise(account_mentions = n()) %>%
  top_n(20) %>%
  arrange(desc(account_mentions))
main_reply_instances

Now we only keep in mastodon_replies the instances registered in main_reply_instances. Then we really get into the network business ! We load the igraph R (to sum it up quickly, let’s say that’s the R counterpart of Gephi) and we transform our data set into a graph of networked interaction.

library(igraph)
mastodon_network <- mastodon_replies %>%
  filter(account_instance %in% main_reply_instances$instance) %>%
  filter(mention_instance %in% main_reply_instances$instance) %>%
  select(account_instance, mention_instance) %>%
  graph_from_data_frame()
mastodon_network
IGRAPH DN-- 20 3672 -- 
+ attr: name (v/c)
+ edges (vertex names):
 [1] mastodon.social    ->mastodon.xyz         maly.io            ->maly.io             
 [3] maly.io            ->maly.io              mastodon.social    ->mastodon.social     
 [5] mastodon.xyz       ->mastodon.xyz         mastodon.social    ->mastodon.social     
 [7] icosahedron.website->mastodon.social      mastodon.social    ->mastodon.social     
 [9] mastodon.social    ->mastodon.social      octodon.social     ->mastodon.social     
[11] mastodon.social    ->mastodon.cloud       maly.io            ->mastodon.social     
[13] icosahedron.website->mastodon.social      icosahedron.website->anticapitalist.party
[15] icosahedron.website->maly.io              octodon.social     ->octodon.social      
+ ... omitted several edges

And a last round or (complicated) calculation. We try to simplify the network (by only removing the self interactions and removing the less interesting one). And since the network remains rather concentrated, we define the sides of the node on a logarithmic scale.

E(mastodon_network)$weight <- 1
mastodon_network <- simplify(mastodon_network, edge.attr.comb=list(weight = "sum", transaction_amount = "sum", function(x)length(x)))
V(mastodon_network)$size <-  10*log10(degree(mastodon_network))
plot(mastodon_network, edge.arrow.size=0.5, layout=layout.fruchterman.reingold, margin=-0.1)

While we clearly see that some instances are more “connected” than others, it is difficult to make out what happens within the core of the networks. Only one mention is enough to create a link between two instances. A good way to clarify theses relationships is to set a higher threshold for displaying a link. Here we have finally opted for a minimal weight of 4 but any other number can be set up to min_weight.

min_weight = 4
mastodon_network_2=delete.edges(mastodon_network, which(E(mastodon_network)$weight <=min_weight))
mastodon_network_2 = delete_vertices(mastodon_network_2, "mastodon.social")
isolates <- which(degree(mastodon_network_2, mode = "all") == 0)
mastodon_network_2 <- delete.vertices(mastodon_network_2, isolates)
E(mastodon_network_2)$color = rgb(1, .8, .8)
plot(mastodon_network_2, edge.arrow.size=0.5, layout=layout.fruchterman.reingold, edge.width=E(mastodon_network_2)$weight)

The network seems to work in concentric circles, with more integrated instances communicating exclusively with more peripheral instances (such as the instance of the Quadrature du net, mamot.fr, with mastodon.gougere.fr). So far, the public timeline appears as the common forum of specific networks of instances — a federation of smaller federations.

Instances = communities ?
Evaluating homogeneity through mentions

The network visualization did not display an important piece of information: the share of “self-mentions” within an instance (it is actually possible to do so, but that would make the network much less legible). That is the fact that the users of mastodon.cloud or mamot.fr may refer more or less exclusively to users of their own instances rather than outside instances. This self-mention rate can be a significant measure, albeit approximative, of the degree of community belonging within an instance.

So let’s define a “sameness instance” variable that will count all the case where account_instance is the same as mention_instance.

sameness_instance <- mastodon_replies %>%
  filter(account_name != mention_name) %>%
  filter(account_instance == mention_instance) %>%
  select(instance = account_instance) %>%
  group_by(instance) %>%
  summarise(same_instance = n())
sameness_rate_instance <- mastodon_replies %>%
  filter(account_name != mention_name) %>%
  select(instance = account_instance) %>%
  group_by(instance) %>%
  summarise(total_instance = n()) %>%
  merge(sameness_instance, by="instance") %>%
  filter(!is.na(instance)) %>%
  mutate(rate = (same_instance/total_instance)*100) %>%
  top_n(10, total_instance)
sameness_rate_instance

Globally the sameness rate is rather high, with a mean of 38.18 for the ten main instances — that is 38% of every notification sent is sent to a user of the same instance. As illustrated in the graph below, it tends to be more significant in networks where there is a higher level of community belonging (such as witches.town).

ggplot(sameness_rate_instance, aes(reorder(instance, -total_instance), rate, fill=instance)) +
  geom_bar(stat = "identity") +
  guides(fill=FALSE) +
  theme(axis.text.x = element_text(size=7)) +
  labs(x="Instance", y="Notification share within an instance", fill="Instance", title="Measuring community belonging with mention shares", caption="Source : Public timeline of Mastodon")

Of course the sameness rate is not inert and may evolve through time: whenever we’ll have more data at our disposal we’ll try to display theses dynamics.

To be continued
LS0tCnRpdGxlOiB8CiAgfCBDdWx0dXJhbCBHZW5lc2lzIGF0IE1hc3RvZG9uICgxLzMpCiAgfCAqT2YgaW5zdGFuY2VzIGFuZCBjb21tdW5pdGllcyoKYXV0aG9yOiAiUGllcnJlLUNhcmwgTGFuZ2xhaXMiCmRhdGU6ICcyMDE3LTA0LTExJwpvdXRwdXQ6CiAgaHRtbF9ub3RlYm9vazogZGVmYXVsdAogIGh0bWxfZG9jdW1lbnQ6IGRlZmF1bHQKICBwZGZfZG9jdW1lbnQ6IGRlZmF1bHQKLS0tCj48ZGl2IHN0eWxlPSJ0ZXh0LWFsaWduOiByaWdodCI+IipUaGlzIGFsbG93cyBwbGFjZXMgd2l0aCBkaWZmZXJlbnQgcnVsZXMgdG8gY29leGlzdCoiIC0tLSBFdWdlbiBSb2Noa28sIFtMZWFybmluZyBmcm9tIFR3aXR0ZXIncyBNaXN0YWtlXShodHRwczovL21lZGl1bS5jb20vQEdhcmdyb24vbGVhcm5pbmctZnJvbS10d2l0dGVycy1taXN0YWtlcy1jMjcyZDY3YmJhNzYpPC9kaXY+Cj48ZGl2IHN0eWxlPSJ0ZXh0LWFsaWduOiByaWdodCI+IipGb2xrbG9yZSB3YXMgc2VsZi1nZW5lcmF0aW5nKiIgLS0tIENoaW5hIE1lbHZpbGxlLCAqS3Jha2VuKjwvZGl2PgoKYGBge3IgaW5jbHVkZSA9IEZBTFNFfQpsaWJyYXJ5KHRpZHl2ZXJzZSkKbGlicmFyeShsdWJyaWRhdGUpCmxpYnJhcnkoZ2dwbG90MikKbGlicmFyeShzdHJpbmdyKQptYXN0b2RvbiA8LSB0YmxfZGYocmVhZF9jc3YyKCJtYXN0b2Rvbl9pbnRlcmFjdGlvbnMuY3N2IikpCmBgYAoKCltNYXN0b2Rvbl0oaHR0cDovL3JtYXJrZG93bi5yc3R1ZGlvLmNvbSkgaXMgdGhlIGxhdGVzdCBvZmZzcHJpbmcgb2YgYSBjb21wbGV4IGFuZCBmYXNjaW5hdGluZyBsaW5lYWdlIG9mIGZyZWUgYW5kIGRlY2VudHJhbGl6ZWQgc29jaWFsIG5ldHdvcmtzLiBXaGlsZSBGYWNlYm9vaywgR29vZ2xlIGFuZCBUd2l0dGVyIGF0dGVtcHRlZCB0byBidWlsZCBsYXJnZSBjb21tZXJjaWFsIGVtcGlyZSBvbiB0aGUgdHJhZGluZyBvZiBzb2NpYWwgaW50ZXJhY3Rpb24sIGZyZWUgc29mdHdhcmUgcHJvamVjdHMgZGlkIG5vdCByZW1haW4gaW5hY3RpdmU6IFtHTlUgU29jaWFsXShodHRwczovL2dudS5pby9zb2NpYWwvKSwgSWRlbnRpLmNhLCBEaWFzcG9yYSwgRnJpZW5kaWNhLgoKTWFzdG9kb24gd2FzIG9ubHkgYSBzbWFsbCBuZXdjb21lciB0byB0aGlzIGNvbm5lY3RlZCBnYWxheHkgb2YgKmNvdW50ZXItc29jaWFsIG5ldHdvcmtzKjogaXQgaXMgYWN0dWFsbHkgYmFzZWQgb24gYSBjb21tb24gc3RhbmRhcmQgZGVmaW5lZCBHbnUgU29jaWFsLCBPU3RhdHVzIC0tLSBzbyB0aGF0IEdOVSBTb2NpYWwgYWNjb3VudHMgY2FuIGNvbW11bmljYXRlIHdpdGggTWFzdG9kb24gYWNjb3VudHMuIEFuZCB0aGUgc3VjY2VzcyBzdG9yeSBjYW1lLCB1bnN1c3BlY3RlZCBhbmQgdW5wcmVjZWRlbnRlZDogaW4gbGVzcyB0aGFuIGEgd2Vla3MsIHdob2xlIGNvbW11bml0aWVzIGhhdmUgc2hpZnRlZCBmcm9tIFR3aXR0ZXIgdG8gTWFzdG9kb24uCgpNYXN0b2RvbiBpcyBub3QgYSBzb2NpYWwgbmV0d29yayBidXQsIHJhdGhlciwgYSBuZXR3b3JrIG9mIHNvY2lhbCBuZXR3b3Jrcy4gQW55b25lIGNhbiBzZXQgdXAgYW5kIGluc3RhbmNlIChldmVuIHRob3VnaCB0aGF0J3MgdGVjaG5pY2FsbHkgY2hhbGxlbmdpbmcgYW5kIGNvc3RseSksIGhvc3QgdGhlIGRhdGEgb2YgdGhlIHBlb3BsZSBqb2luaW5nIGluIGFuZCBkZWZpbmUgc3BlY2lmaWMgcnVsZXMgKGZvciBpbnN0YW5jZSByZWdhcmRpbmcgbW9kZXJhdGlvbikgOyBhbGwgdGhlIGluc3RhbmNlcyBhcmUgbGlua2VkIGludG8gYSAiZmVkZXJhdGlvbiIgKGFuZCBjb250aW51b3VzbHkgZXhjaGFuZ2UgdGV4dHMgYW5kIGludGVyYWN0aW9uKS4gV2hpbGUgdGhpcyBpbmZyYXN0cnVjdHVyZSBkZXNpZ25lZCBieSBFdWdlbiBSb3Roa28gYW5kIGEgdGVhbSBvZiB2b2x1bnRlZXIgZGV2ZWxvcGVycyBpcyBub3QgcmV2b2x1dGlvbmFyeSAqcGVyIHNlKiBpdCBoYXMgYmVlbiBlbGVnYW50bHkgcmVmaW5lZCAod2l0aCwgZm9yIGluc3RhbmNlLCBhIG11Y2ggZnJpZW5kbGllciBpbnRlcmZhY2UgdGhhbiB0aGUgdmFuaWxsYSBUd2l0dGVyIGFuZCBtdWNoIG1vcmUgZGl2ZXJzZSBwcml2YWN5IGNvbnRyb2xzKS4gCgpNYXN0b2RvbiBjbGFpbXMgdG8gc29sdmUgYSB3aWRlIGFycmF5IG9mIHN0cnVjdHVyYWwgc2hvcnRjb21pbmdzIHdpdGhpbiBzb2NpYWwgbmV0d29ya3M6IGEgc3RydWN0dXJhbCBpbmFiaWxpdHkgdG8gZGVmaW5lIHRoZSBwcm9wZXIgYmFsYW5jZSBiZXR3ZWVuIGNlbnNvcnNoaXAgYW5kIHRoZSBkaXNzZW1pbmF0aW9uIG9mIGhhdGUgc3BlZWNoIGFuZCBoYXJhc3NlbWVudCwgYSBtYXNzaXZlIGV4cGxvaXRhdGlvbiBvZiBwcml2YXRlIGludGVyYWN0aW9ucywgYW4gaW50ZXJmYWNlIG1vcmUgYW5kIG1vcmUgbW9kZWxsZWQgYXJvdW5kIGFkdmVydGlzaW5nIHJldHVybnPigKYgQ29uc2VxdWVudGx5LCBub2JvYnkgIm93bnMiIE1hc3RvZG9uIGFzIGEgd2hvbGUgKGFuZCBoYXMgdGhlIGFiaWxpdHkgdG8gc2hpZnQgaXQgdG8gYSBjb21tZXJjaWFsIG1vZGVsKSBhbmQgZWFjaCBpbnN0YW5jZSBjYW4gc2V0IHVwIHRoZWlyIG93biBjb2RlLCB0aGF0IGNhbiBtZWV0IHRoZSBuZWVkcyBvZiBzcGVjaWZpYyBwZW9wbGUgYW5kIGNvbW11bml0aWVzLgoKVGhpcyBwcm9taXNlIGlzIHRoZSBzdGFydGluZyBwb2ludCBvZiBteSBleHBsb3JhdG9yeSByZXNlYXJjaC4gSSBoeXBvdGhlc2l6ZSB0aGF0IE1hc3RvZG9uIGlzIAlpbnRyaW5zaWNhbGx5IGJ1aWxkIHRvIGZhdm9yIHRoZSBnYXRoZXJpbmcgb2YgY29tbXVuaXRpZXMuIFRoZSB0ZWNobmljYWwgZGVzaWduIG9mIGluc3RhbmNlcyBpbW1lZGlhdGVseSByYWlzZXMgZ291dmVybmFuY2UgaXNzdWVzICh3aGF0IGhhcyB0byBiZSByZWd1bGF0ZWQ/IGFuZCBob3cgdG8gcmVndWxhdGUgaXQ/KSB3aGljaCBpbiB0dXJuIGZhdm9ycyB0aGUgZXhwcmVzc2lvbiwgaWYgbm90IHRoZSBnZW5lc2lzIG9mIGEgc3BlY2lmaWMgImN1bHR1cmUiIC0tLSBtYWRlIHBlcmNlcHRpYmxlIGZyb20gdGhlIG91dHNpZGUgYnkgdGhlIHVzZSBvZiBzaGFyZWQgbGFuZ3VhZ2VzIGFuZCByZWZlcmVuY2VzLiBUaGlzICpjdWx0dXJhbCBnZW5lc2lzKiBpcyB0YWNpdGx5IFthY2tub3dsZWRnZWRdKGh0dHBzOi8vbWVkaXVtLmNvbS9AR2FyZ3Jvbi9sZWFybmluZy1mcm9tLXR3aXR0ZXJzLW1pc3Rha2VzLWMyNzJkNjdiYmE3NikgYnkgTWFzdG9kb24ncyBpbml0aWF0b3IsIEV1Z2VuIFJvY2hrbzoKCj5EaWZmZXJlbnQgaW5zdGFuY2VzLCBvd25lZCBieSBkaWZmZXJlbnQgZW50aXRpZXMsIHdpbGwgaGF2ZSBkaWZmZXJlbnQgcnVsZXMgYW5kIG1vZGVyYXRpb24gcG9saWNpZXMuIFRoaXMgZ2l2ZXMgdGhlIHBvd2VyIHRvIHNoYXBlIHNtYWxsZXIsIGluZGVwZW5kZW50LCB5ZXQgaW50ZWdyYXRlZCBjb21tdW5pdGllcyBiYWNrIHRvIHRoZSBwZW9wbGUuCgpBbmQgaXQgaXMgbm90IGEgbmV3IHByb2Nlc3Mgb24gdGhlIHdlYi4gTGFyZ2Ugc2NhbGUgcHJvamVjdHMsIHN1Y2ggYXMgV2lraXBlZGlhIG9yIERlYmlhbiwgaGFkIGEgc2ltaWxhciB0ZW5kZW5jeSB0byBkZXZlbG9wIGEgc3Ryb25nICJzZW5zZSBvZiBjb21tdW5pdHkiLCBtZXJlbHkgZnJvbSB0aGUgbmVjZXNzaXR5IHRvIGNvcGUgd2l0aCBjb21tb24gcHJvYmxlbXMgYW5kIHRvIHNvbHZlIHRoZW0gaW4gY29tbW9uLgoKQWx0aG91Z2ggdGhpcyByZXNlYXJjaCBjbGFpbXMgdG8gYmUgb2YgYW4gImV4cGxvcmF0b3J5IiBuYXR1cmUsIGl0IG1heSBzZWVtcyBhIGJpdCBwYXJhZG94aWNhbCB0byBzdHVkeSBhIG5ldHdvcmsgdGhhdCBoYXMgb25seSBnb25lIG1haW5zdHJlYW0gZm9yIHNldmVyYWwgZGF5cyBhbmQgcmVtYWlucyBieSBhbGwgYWNjb3VudHMgYXQgYSB2ZXJ5IGVhcmx5IHN0YWdlIG9mIGRldmVsb3BtZW50LiBBIG5ldyBmb3JtIG9mIHNjaWVudGlmaWMgd3JpdGluZyBtYWtlcyB0aGlzIGluaXRpYXRpdmUgYSBiaXQgbW9yZSBhcHByb3ByaWF0ZTogdGhlIGNvZGUgbm90ZWJvb2suIAoKVGhpcyBpcyBub3QgYSBkZWZpbml0ZSBhcnRpY2xlIGJ1dCB0aGUgdHJhbnNsYXRpb24gb2YgYSB3b3JrIGluIHByb2dyZXNzLiBBbGwgdGhlIHRleHQgaXMgc3RydWN0dXJlZCBhcm91bmQgImNvZGUgYmxvY2tzIiBpbiBSIHRoYXQgY2FuIGJlIGVhc2lseSBjaGFuZ2VkIGFuZCBlYXNpbHkgY2hhbGxlbmdlZDogYWx0ZXJpbmcgb25lIHZhcmlhYmxlIG9yIG9uZSBmdW5jdGlvbiBtYXkgZnVsbHkgdHJhbnNmb3JtIHRoZSBkYXRhc2V0IG9yIGEgZ3JhcGggYW5kIGdlbmVyYXRlcyBuZXcgaW5zaWdodC4gVGhlIG9yaWdpbmFsIGNvZGUsIGRhdGEgYW5kIHJtZCBmaWxlcyBhcmUgYXZhaWxhYmxlIG9uIFtHaXRodWJdKGh0dHBzOi8vZ2l0aHViLmNvbS9Eb3JpYWxleGFuZGVyL01hc3RvZG9uQ3VsdHVyYWxHZW5lc2lzKSBhbmQgY2FuIGJlIHJ1biBhZ2FpbiAob3IgdHdlYWtlZCwgZm9yIHRoYXQgbWF0dGVyKSBvbiBSIHN0dWRpby4gCgo8IS0tVGhpcyBpcyBhIGR5bmFtaWMgdGV4dCAoc29tZSB3b3JkcyBhbmQgZmlndXJlcyBhcmUgZXZlbiBnZW5lcmF0ZWQgIm9uIHRoZSBmbHkiIGJ5IFIgY29kZSkgd2hpY2ggY2FuIGRlYWwgd2l0aCBkeW5hbWljIGRhdGEuIEZvciB0aGUgdGltZSBiZWluZywgSSBoYXZlIG9ubHkgcmV0cmlldmVkIHRocmVlIGRheXMgb2YgdGhlICJwdWJsaWMgdGltZWxpbmUiIG9mIE1hc3RvZG9uLiBTaW5jZSBtb3N0IG9mIHRoZSBkZXZlbG9wbWVudHMgb2YgdGhlIG5ldHdvcmsgbGF5cyBpbiB0aGUgZnV0dXJlIEkgaGF2ZSwgYXQgdGhlIHRpbWUgb2YgdGhpcyB3cml0aW5nLCBubyB3YXkgdG8gYXNzZXNzIHdoYXQgd2lsbCBiZSBpdHMgZGVmaW5pbmcgZmVhdHVyZS4gSSBjYW4gc3RpbGwgaW1wbGVtZW50IGEgcmV1c2FibGUsIGdlbmVyYWwgcHVycG9zZSBwcm9ncmFtbWluZyBzdHJ1Y3R1cmUgdGhhdCBjYW4gZ2l2ZSBhIGdsaW1wc2Ugb2Ygd2hhdCBpcyB0byBjb21lLi0tPiAKCkknbSBsaWtlbHkgdG8gc3ViZGl2aWRlIHRoaXMgcmVzZWFyY2ggaW50byB0aHJlZSBlcGlzb2RlcyAoYnV0IGV2ZW4gdGhhdCBjYW4gY2hhbmdlKS4gVGhpcyBmaXJzdCBwYXJ0IG1vc3RseSBkZWFscyB3aXRoIHRoZSAiaW50ZXJhY3Rpb25zIiB3aXRoaW4gdGhlIG5ldHdvcmtzIChkbyB0aGUgbWVudGlvbnMgcmVmbGVjdCBzb21lIHByZWZlcmVuY2VzIHRvd2FyZCB0aGUgdXNlciBvZiBhIHNhbWUgaW5zdGFuY2U/KS4gVGhlIHNlY29uZCBwYXJ0IHdpbGwgbG9vayBhdCB0aGUgY3VsdHVyYWwgZ2VuZXNpcyBpc3N1ZSB0aHJvdWdoIHRoZSBsZW5zZXMgb2YgKnRleHQgbWluaW5nKiAoYXJlIHRoZSB0ZXh0dWFsIGNvbnRlbnQgb2YgdG9vdHMgc29tZXdoYXQgcmVsYXRlZCBhY2Nyb3NzIGFuIGluc3RhbmNlPyB0byB3aGF0IGV4dGVudCBkb2VzIGFuIGluc3RhbmNlICpzcGVjaWFsaXplKiBvbiBzb21lIGlzc3VlcyBvciB0aGVtYXRpY3M/KS4gVGhlIGZpbmFsIHBhcnQgaXMgc3RpbGwgZnV6enkgYnV0IHdpbGwgYXQgbGVhc3QgZHJhdyBzb21lIGJyb2FkZXIgYW5hbHlzaXMgKGJ5IGNvbXBhcmluZyBNYXN0b2RvbiB3aXRoIHByZWNlZGVudHMgaW4gb3RoZXIgZnJlZSB3ZWIgY29tbXVuaXRpZXMgb3IgY29udGV4dHVhbGl6aW5nIGl0cyBwYXJ0IHdpdGhpbiB0aGUgd2lkZXIgcGxhdGZvcm0gY29vcGVyYXRpdmlzbSBtb21lbnQpLgoKIyNBIGZyaWVuZGx5IEFQSQoKQSBrZXkgZmVhdHVyZSBvZiBNYXN0b2RvbiBkaWQgbm90IGZ1bGx5IGdldCB0aGUgYXBwcmFpc2FsIGl0IGRlc2VydmVzOiB0aGUgQVBJLiBXaGlsZSBiZWluZyBpbnN0cnVtZW50YWwgdG8gdGhlIGRldmVsb3BtZW50IG9mICpUd2l0dGVyIFN0dWRpZXMqLCBUd2l0dGVyJ3MgQVBJIGhhcyBiZWNvbWUgbW9yZSBhbmQgbW9yZSBjb252b2x1dGVkLCBhcyB0aGUgc29jaWFsIG5ldHdvcmsgZm9jdXNlZCBpdHMgZWNvbm9taWMgZGV2ZWxvcG1lbnQgcGxhbiBvbiB0aGUgbW9uZXRpemF0aW9uIG9mIHVzZXJzJyBkYXRhLiAKCkluIGl0cyBjdXJyZW50IGZvcm0sIE1hc3RvZG9uIGlzIGZ1bGx5IG11Y2ggbW9yZSBmcmllbmRseSB0byB0aGUgY2FzdWFsIHRleHQgJiBkYXRhIG1pbmVyLiBFdmVuIHRob3VnaCB0aGUgc29jaWFsIG5ldHdvcmsgaXMgaW4gaXRzIHByaW1lLCB0aGVyZSdzIGFscmVhZHkgZGVkaWNhdGVkIGxpYnJhcmllcyB0byBkZWFsIHdpdGggdGhlIEFQSSBpbiBSdWJ5IGFuZCBQeXRob24uIFNpbmNlIHRoZSBbMS4xLjEgdXBkYXRlXShodHRwczovL2dpdGh1Yi5jb20vdG9vdHN1aXRlL21hc3RvZG9uL3JlbGVhc2VzL3RhZy92MS4xLjEpLCB0aGVyZSBpcyBubyBuZWVkIHRvIGxvZ2luIHRvIGFjY2VzcyB0byB0aGUgcHVibGljIHRpbWVsaW5lOiBldmVyeW9uZSBjYW4gc2V0IHVwIGFuIEFQSSBrZXkgaW4gYSBmZXcgc2Vjb25kcyAodGhhdCdzIHRoZSBwdXJwb3NlIG9mIHRoZSBjcmVhdGVfbWFzdG9kb25fYXBwLnB5IHNjcmlwdCwgdG8gYmUgcnVuIGp1c3Qgb25jZSkuIAoKVGhlIHB1YmxpYyB0aW1lbGluZSBkbyAqKm5vdCoqIHJlZ2lzdGVyIGFsbCB0aGUgcHVibGljYXRpb25zIG9uIE1hc3RvZG9uOiBldmVyeSB0b290IHdpdGggcHJpdmFjeSBzZXR0aW5nLCBldmVyeSBkZWxldGVkIHRvb3QgYW5kLCBsaWtlbHksIGV2ZXJ5IGluc3RhbmNlIG5vdCBjb25uZWN0ZWQgdG8gbWFzdG9kb24uc29jaWFsIGRvIG5vdCBzaG93IHVwLiBCYXNlZCBvbiB0aGUgdG9vdCBpZCwgaXQgc2VlbXMgdGhhdCBteSBjb3Jwb3JhIHJlcHJlc2VudHMgYHIgcm91bmQoKChucm93KG1hc3RvZG9uKS8obWFzdG9kb24kdG9vdF9pZFt3aGljaC5tYXgobWFzdG9kb24kdG9vdF9pZCldLW1hc3RvZG9uJHRvb3RfaWRbd2hpY2gubWluKG1hc3RvZG9uJHRvb3RfaWQpXSkpKjEwMCksIDIpYCUgb2YgYWxsIHRvb3RzIChhcyBzdGF0ZWQgZWFybGllciB0aGF0IHZhbHVlIGlzIGdlbmVyYXRlZCBieSBSIGFuZCBjYW4gY2hhbmdlIGRlcGVuZGluZyBvbiB0aGUgYXZhaWxhYmxlIGRhdGEpLiBUaGlzIGlzIGZhciBmcm9tIGV4aGF1c3RpdmUgYnV0IHN0aWxsIGVub3VnaCB0byBkZWR1Y2Ugc29tZSBpbnRlcmVzdGluZyBkeW5hbWljcy4KCk5vdyB0aGVyZSdzIGEgZnVuY3Rpb24gaW4gcGFydGljdWxhciB0aGF0IG1hZGUgbWUgZmFsbCBpbiBsb3ZlIHdpdGggdGhpcyBBUEk6ICJwYWNlIi4gQSByZWN1cnJpbmcgcHJvYmxlbSBJIGhhZCB3aGVuIGFuYWx5emluZyBBUEkgZGF0YSwgaXMgdGhhdCBpdCBpcyBuZWNlc3NhcnkgdG8gc2V2ZXJpbHkgcmVzdHJhaW4gdGhlIHJhdGUgb2YgYXV0b21hdGljIGV4dHJhY3Rpb24uIFdoaWxlIGxpbWl0aW5nIHRoZSBudW1iZXIgb2YgY2FsbHMgdG8gdGhlIEFQSSB0byByZWxpZXZlIHRoZSBzZXJ2ZXIgaXMgcGVyZmVjdGx5IHVuZGVyc3RhbmRhYmxlLCBpdCBmcmVxdWVudGx5IGVuZHMgdXAgaW4gYWRkaW5nIHVubmVjZXNzYXJ5IHNlY29uZHMgb2YgInNsZWVwIiB0byB0aGUgY29kZSAoZm9yIGluc3RhbmNlLCB0aHJvdWdoIHRpbWUuc2xlZXAoKSkuIFdpdGggcGFjZSwgYWxsIHRoZSB3YWl0IGlzIG9wdGltaXplZCBmcm9tIGJvdGggc2lkZTogdGhlIHNlcnZlciBzbG93cyBkb3duIHdoZW5ldmVyIHRoZXJlIGlzIHRvbyBtdWNoIHByZXNzdXJlIGFuZCBhY2NlbGVyYXRlcyBvdGhlcndpc2UuIEl0IGlzIG5vdCBuZWNlc3NhcnkgdG8gZGV2aXNlIGRldmlvdXMgdGFjdGljcyBpbiBvcmRlciB0byB0YWtlIHRoZSBtYXggb3V0IHRoZSBBUEkgY2FsbHMgd2hpbGUgYXZvaWRpbmcgZ2V0dGluZyBiYW4uCgpgYGB7cHl0aG9uIGV2YWwgPSBGQUxTRX0KbWFzdG9kb24gPSBNYXN0b2RvbihjbGllbnRfaWQgPSAncHl0b290ZXJfY2xpZW50Y3JlZC50eHQnLCByYXRlbGltaXRfbWV0aG9kPSdwYWNlJykKYGBgCgpUaGVyZSdzIGxpdHRsZSB0byBzYXkgYWJvdXQgdGhlIHR3byBweXRob24gc2NyaXB0cyB1c2VkIHRvIHJldHJpZXZlIHRoZSBkYXRhIChtYXN0b2Rvbl9yZXRyaWV2ZV9BUEkucHkpIGFuZCB0byBwdXQgdGhlIG1vcmUgaW50ZXJlc3RpbmcgcGllY2VzIG9mIGluZm9ybWF0aW9uIGluIGEgY3N2IChtYXN0b2Rvbl9leHRyYWN0X2pzb24ucHkpLiBUaGUgZmlyc3Qgc2NyaXB0IGRlZmluZSBhIGxvb3Agb2YgMTAwIGNhbGxzIHdpdGggYSBzdGFydGluZyBpZCAod2hpY2ggaGFzIHRvIGJlIGNoYW5nZWQgYXQgdGhlIGVuZCBvZiBlYWNoIHJ1biwgYnkgdXNpbmcgdGhlIGxhc3QgaWQgcHJpbnRlZCBvbiB0aGUgdGVybWluYWwsIG9yIHRoZSBuYW1lIG9mIHRoZSBsYXN0IGZpbGUgaW4gdGhlIGNvcnB1cyBkaXJlY3RvcnkpLiBJdCB0YWtlcyBhYm91dCBmaXZlIG1pbnV0ZXMgdG8gbWFrZSB0aGUgMTAwIGNhbGxzIC0tLSBldmVuIHRob3VnaCB0aGF0IG1heSBjaGFuZ2UgZGVwZW5kaW5nIG9uICJyYXRlIi4gRWFjaCBjYWxscyByZXRyaWV2ZSAyMCAidG9vdHMiIC0tLSB0aGUgTWFzdG9kb24gY291bnRlcnBhcnQgb2YgdHdlZXRzLiBJIGhhdmUgYWxzbyBpbmNsdWRlZCBhIHRpbWUuc2xlZXAgb2YgMTAgc2Vjb25kcyB3aXRoIGFuIGV4Y2VwdGlvbjogZnJvbSB0aW1lIHRvIHRpbWUgYSAqZ2VuZXJhbCBBUEkgZXJyb3IqIHBvcHMgdXAgb24gdGhlIHNlcnZlciBzaWRlICh3aGljaCBoYXMgYmVjb21lIG11Y2ggbGVzcyBmcmVxdWVudCB3aXRoIHRoZSAxLjEuMSB1cGRhdGUpLgoKYGBge3B5dGhvbiBldmFsID0gRkFMU0V9CmZvciBwYXNzaW5nIGluIHJhbmdlKDAsMTAwKToKCXRyeToKCQlwcmludCgiZ2V0dGluZyB0aGUgbWFzdG9kb24gdGltZWxpbmUganNvbiIpCgkJdGltZWxpbmVfbWFzdG9kb24gPSBtYXN0b2Rvbi50aW1lbGluZV9wdWJsaWMobWF4X2lkID0gbGFzdF9pZCkKCQlqc29uX21hc3RvZG9uID0ganNvbi5kdW1wcyh0aW1lbGluZV9tYXN0b2RvbikKCQlsYXN0X2lkID0gdGltZWxpbmVfbWFzdG9kb25bbGVuKHRpbWVsaW5lX21hc3RvZG9uKS0xXVsiaWQiXQoJCXByaW50KCJsYXN0IHN0b3AgYXQgdG9vdCBpZCAiICsgc3RyKGxhc3RfaWQpKQoJCWpzb25fdGl0bGUgPSAibWFzdG9kb25fY29ycHVzLyIgKyBzdHIobGFzdF9pZCkgKyAiLmpzb24iCgkJanNvbl9zYXZlX21hc3RvZG9uID0gb3Blbihqc29uX3RpdGxlLCAndycpCgkJanNvbl9zYXZlX21hc3RvZG9uLndyaXRlKGpzb25fbWFzdG9kb24pCglleGNlcHQ6CgkJcHJpbnQoIkFQSSBlcnJvciwgd2FpdGluZyBhIGJpdCB0byBzZWUgd2hhdCBoYXBwZW5zIikKCQl0aW1lLnNsZWVwKDEwKQpgYGAKClRoZSBzZWNvbmQgc2NyaXB0IGV4dHJhY3QgYW5kIGNsZWFuIHRoZSBqc29uIGZpbGVzIGluIGEgc29tZXdoYXQgdWdseSBtYW5uZWVyIC0tLSB0aGF0J3Mgbm90IHRoZSBjb2RlIEknbSB0aGUgbW9zdCBwcm91ZCBvZiwgYnV0IEkgd2FzIGxvb2tpbmcgZm9yd2FyZCB0byB0aGUgcHJvcGVyIGRhdGEgYW5hbHlzaXMuCgpCZWZvcmUgdGhhdCwganVzdCBhIHNtYWxsIGNhdmVhdCBvbiB0aGUgaXNzdWUgb2YgInBlcnNvbmFsIGRhdGEiLiBXaGlsZSB0aGlzIGlzIHRoZSAqcHVibGljKiB0aW1lbGluZSBvZiBNYXN0b2RvbiBhbmQsIHdpdGggYSBsaXR0bGUgdGltZSwgYW55b25lIGNhbiByZXRyaWV2ZSB0aGUgc2ltaWxhciBpbmZvcm1hdGlvbiB0aHJvdWdoIHRoZSBBUEksIEknbSBzdGlsbCBhIGxpdHRsZSB1bmNlcnRhaW4gb2Ygd2hhdCBpcyB0aGUgcHJvcGVyIGV0aGljcyB3YXkgb2YgZGVhbGluZyB3aXRoIHNvY2lhbCBpbnRlcmFjdGlvbnMgb2YgInJlYWwgcGVvcGxlIiAoYWxsIHRoZSBtb3JlIGFzIGNvbmNlcm4gZm9yIHByaXZhY3kgaXMgb25lIG9mIHRoZSBsZWFkaW5nIGNvbmNlcm4gb2YgTWFzdG9kb24pLiBGb3IgdGhlIHRpbWUgYmVpbmcsIHRoZSBkYXRhc2V0IGF2YWlsYWJsZSBvbiB0aGlzIGdpdGh1YiBkaXJlY3Rvcnkgb25seSBrZWVwcyB0aGUgaW5mb3JtYXRpb24gYWN0dWFsbHkgdXNlZCBpbiB0aGlzIGNvZGUgbm90ZWJvb2sgKHdoaWNoIGV4Y2x1ZGVzIHRoZSB0ZXh0dWFsIGNvbnRlbnQgb2YgZWFjaCBwdWJsaWNhdGlvbiwgdGhlICJ0b290cyIpLiBJIGhhdmUgYWxzbyByZXBsYWNlZCB0aGUgcGVyc29uYWwgbmFtZSBvZiBlYWNoIGFjY291bnQgYnkgdGhlaXIgaWQ6IHdoaWxlIHRoZSBkYXRhIGlzIG5vdCBhbm9ueW1pbml6ZWQsIGl0IHdpbGwgYmUgbmVjZXNzYXJ5IHRvIGNhbGwgdGhlIEFQSSBvbmNlIG1vcmUgdG8gZ2V0IGFueSByZWxldmFudCBwZXJzb25hbCBpbmZvcm1hdGlvbi4KCiMjRmlyc3Qgc3RlcHMgaW4gUgoKQW5kLCBkYXRhIGFuYWx5c2lzLCBoZXJlIHdlIGNvbWUgISBVbmV4cGVjdGVkbHksIG91ciBmaXJzdCBzdGVwcyBhaW1zIGF0IGxvYWRpbmcgYSBjb3VwbGUgb2YgbGlicmFyaWVzIGFuZCBjbGVhbmluZyBhIGJpdCB0aGUgY3N2IGZpbGUgZGVsaXZlcmVkIGJ5IHRoZSBweXRob24gc2NyaXB0LgoKYGBge3IgZXZhbCA9IEZBTFNFfQpsaWJyYXJ5KHRpZHl2ZXJzZSkKbGlicmFyeShsdWJyaWRhdGUpCmxpYnJhcnkoZ2dwbG90MikKbGlicmFyeShzdHJpbmdyKQptYXN0b2RvbiA8LSB0YmxfZGYocmVhZF9jc3YyKCJtYXN0b2Rvbl9pbnRlcmFjdGlvbnMuY3N2IikpCmBgYAoqcmVhZF9jc3YyKiB0cmllcyB0byAiZ3Vlc3MiIHRoZSBvcmlnaW5hbCBmb3JtYXQgb2YgdGhlIHZhbHVlcyAod2hldGhlciBpdCBpcyBhIGNoYXJhY3RlciwgYW4gaW50ZWdlcuKApikuIFRoZSBwcm9jZXNzIGlzIGVmZmljaWVudCwgYnV0IG5vdCBmdWxsIHByb29m4oCmCgpGb3IgaW5zdGFuY2UgdGhlIHR3byBkYXRlIHZhbHVlcyAocHVibGljYXRpb24gb2YgdGhlIHRvb3QsIGFuZCBjcmVhdGlvbiBvZiB0aGUgTWFzdG9uYXV0IGFjY291bnQpIGFyZSBlbmNvZGVkIGFzIGNoYXJhY3RlcnMuIEZvcnR1bmF0ZWx5LCB0aGV5IGFyZSBhbHNvIGluIGEgc3RhbmRhcmQgZm9ybWF0OiB5bWRfaG1zIChmcm9tIGx1YnJpZGF0ZSkgY2FuIGVhc2lseSBwYXJzZSB0aGVtLiBXZSBhbHNvIGNyZWF0ZSBhIG5ldyBjb2x1bW4gY3JlYXRlZF9hdF9ob3VyLCB0aGF0IG9ubHkga2VlcHMgdGhlIGhvdXJzIGZvciBlYWNoIGRheSAoYW5kIHJlbW92ZSB0aGUgbWludXRlcyBhbmQgc2Vjb25kcykuIEFuZCBieSB0aGUgd2F5IHdlIGVuc3VyZSB0aGF0IGFsbCB0aGUgZGF0ZXMgYXJlIHJlZ2lzdGVyZWQgKmFmdGVyKiAiMjAxNy0wNC0wNiAwNTowMDowMCIgKHRoZXJlIGFyZSBzb21lIG91dGxpZXJzLCBwcm9iYWJseSBkdWUgdG8gdGhlICJib29zdCIgb2Ygb2xkZXIgdG9vdHMpLgoKYGBge3IgbWVzc2FnZSA9IEZBTFNFLCB3YXJuaW5nID0gRkFMU0V9Cm1hc3RvZG9uIDwtIG1hc3RvZG9uICU+JSAKICBtdXRhdGUoY3JlYXRlZF9hdF9ob3VyID0gc3RyX3N1YihjcmVhdGVkX2F0LCAwLCAtMTIpKSAlPiUgCiAgbXV0YXRlKGNyZWF0ZWRfYXRfaG91ciA9IHltZF9oKGNyZWF0ZWRfYXRfaG91cikpICU+JQogIG11dGF0ZShjcmVhdGVkX2F0ID0geW1kX2htcyhjcmVhdGVkX2F0KSkgJT4lIAogIGZpbHRlcihjcmVhdGVkX2F0ID4geW1kX2htcygiMjAxNy0wNC0wNiAwNTowMDowMCIpKQpgYGAKCk5leHQsIHdlIGhhdmUgdG8gInNlcGFyYXRlIiB0aGUgaW5zdGFuY2UgY2hvc2VuIGJ5IHRoZSB1c2VyIGZyb20gaGlzIG5hbWUuIFRoZSAqQCogcHJvdmlkZXMgYW4gdW5hbWJpZ3VvdXMgd2F5IHRvIHNwbGl0IHRoZSBzdHJpbmdzIGFuZCByZWRlZmluZSB0d28gbmV3IGNvbHVtbnM6IGFjY291bnRfaW5zdGFuY2UgYW5kIGFjY291bnRfbmFtZS4gU2luY2Ugd2Ugb3BlcmF0ZSBmcm9tIGEgbWFzdG9kb24uc29jaWFsIHRpbWVsaW5lLCB0aGUgbWFzdG9kb24uc29jaWFsIGluc3RhbmNlIGlzIGltcGxpZWQ6IHdlIHJlcGxhY2UgYWNjb3JkaW5nbHkgdGhlIG5vbiB2YWx1ZXMgZ2VuZXJhdGVkIG9uIGFjY291bnRfaW5zdGFuY2UuCgpgYGB7ciBtZXNzYWdlID0gRkFMU0UsIHdhcm5pbmcgPSBGQUxTRX0KbWFzdG9kb24gPC0gbWFzdG9kb24gJT4lIAogIHNlcGFyYXRlKGFjY291bnQsIGMoImFjY291bnRfbmFtZSIsICJhY2NvdW50X2luc3RhbmNlIiksIHNlcD0iQCIpICU+JSAKICBtdXRhdGUoYWNjb3VudF9pbnN0YW5jZSA9IGlmZWxzZShpcy5uYShhY2NvdW50X2luc3RhbmNlKSwgIm1hc3RvZG9uLnNvY2lhbCIsIGFjY291bnRfaW5zdGFuY2UpKQpgYGAKCkEgZmlyc3QgbG9vayB0byBvdXIgZGF0YWZyYW1lIHN1Z2dlc3RzIHRoYXQgYWxsIHNlZW1zIGluIG9yZGVyIDoKCmBgYHtyfQpzZWxlY3QobWFzdG9kb24sIHRvb3RfaWQsIGNyZWF0ZWRfYXQsIGNyZWF0ZWRfYXRfaG91ciwgYWNjb3VudF9uYW1lKQpgYGAKCiMjIEFuIEV1cm9wZWFuLWNlbnRlcmVkIG5ldHdvcmsgPGJyPiBJbnNpZ2h0cyBmcm9tIHRoZSBkYXkvbmlnaHQgR01UIGNvbnRyYXN0CgpTbyB3ZSBoYXZlIGEgbWlkZGxlLXNpemVkIGRhdGEgZnJhbWUgb2YgYHIgbnJvdyhtYXN0b2RvbilgIHRvb3RzIHRoYXQgZ29lcyBmcm9tIGByIG1hc3RvZG9uJGNyZWF0ZWRfYXRbd2hpY2gubWluKG1hc3RvZG9uJGNyZWF0ZWRfYXQpXWAgdG8gYHIgbWFzdG9kb24kY3JlYXRlZF9hdFt3aGljaC5tYXgobWFzdG9kb24kY3JlYXRlZF9hdCldYCB3aGljaCBpcyByZWFkeSBmb3Igc29tZSBzaW1wbGUgdGVtcG9yYWwgZGF0YXZpei4gVGhhbmtzIHRvIG91ciBjcmVhdGVkX2F0X2hvdXIgY29sdW1uIHdlIGFyZSBhYmxlIHRvIGRpc3BsYXkgdGhlIGV2b2x1dGlvbiBvZiB0aGUgZ2xvYmFsIHBhcnRpY2lwYXRpb24gb24gdGhlIHB1YmxpYyB0aW1lbGluZSBkdXJpbmcgdGhlIHBlcmlvZC4gVGhlIGRheS9uaWdodCBjb250cmFzdCBpcyBub3RpY2VhYmx5IHN0cm9uZy4gU2luY2UgdGhlIHRpbWUgaXMgZXhwcmVzc2VkIGlzIEdNVCwgdGhlIGRlbW9ncmFwaHkgb2YgdGhlIE1hc3RvbmF1dHMgaXMgbGlrZWx5IGNlbnRlcmVkIG9uIEV1cm9wZWFuIGNvdW50cmllcy4gTXkgcGVyc29uYWwgZXhwZXJpZW5jZSByZWxhdGVzIHRvIHRoaXMgaHlwb3RoZXNpczogc28gZmFyIHRoZSBBbWVyaWNhbiBhY2NvdW50cyBJIGZvbGxvdyBvbiBUd2l0dGVyIGhhdmUgbm90IG1pZ3JhdGVkIHlldOKApgoKYGBge3J9Cm1hc3RvZG9uICU+JSAKICBmaWx0ZXIoY3JlYXRlZF9hdF9ob3VyICE9IGNyZWF0ZWRfYXRfaG91clt3aGljaC5taW4oY3JlYXRlZF9hdF9ob3VyKV0pICU+JQogIGZpbHRlcihjcmVhdGVkX2F0X2hvdXIgIT0gY3JlYXRlZF9hdF9ob3VyW3doaWNoLm1heChjcmVhdGVkX2F0X2hvdXIpXSkgJT4lCiAgZ3JvdXBfYnkoY3JlYXRlZF9hdF9ob3VyKSAlPiUKICBzdW1tYXJpc2Uobl9ob3VyPW4oKSkgJT4lCiAgZ2dwbG90KGFlcyhjcmVhdGVkX2F0X2hvdXIsIG5faG91cikpICsgCiAgZ2VvbV9saW5lKGNvbG9yPSJyZWQiLCBzaXplPTEpICsKICB5bGltKDAsMTUwMCkgKwogIGxhYnMoeD0iSG91ciIsIHk9Ik51bWJlciBvZiB0b290cyIsIHRpdGxlPSJQYXJ0aWNpcGF0aW9uIHdpdGhpbiBhbGwgaW5zdGFuY2VzIG9mIE1hc3RvZG9uIiwgY2FwdGlvbj0iU291cmNlIDogUHVibGljIHRpbWVsaW5lIG9mIG1hc3RvZG9uLnNvY2lhbCIpCmBgYAoKWWV0LCBNYXN0b2RvbiBpcyBub3Qgb25lIGJpZyBjZW50cmFsaXplZCBuZXR3b3JrIGJ1dCBhICJmZWRhcmF0aW9uIiBvZiBpbmRpdmlkdWFsaXplZCBpbnN0YW5jZXMuIE91ciB2YXJpYWJsZSBtYWluX2luc3RhbmNlcyBnZXQgdGhlIG5hbWUgb2YgdGhlIG1vc3QgYWN0aXZlIGluc3RhbmNlcy4gSGVyZSBJIGhhdmUgc2VsZWN0ZWQgdGhlIHRvcCA2IGluc3RhbmNlcyAoKSBtaW51cyAqbWFzdG9kb24uc29jaWFsKi4gWW91IGNhbiBlYXNpbHkgY2hhbmdlIHRoZXNlcyBzZXR0aW5ncyBieSBjaG9zaW5nIGEgZGlmZmVyZW50IHRocmVzaG9sZCBvbiB0b3BfbiBvciByZW1vdmluZyB0aGUgKmZpbHRlcihhY2NvdW50X2luc3RhbmNlID0hICJtYXN0b2Rvbi5zb2NpYWwiKSAlPiUqIGlmIHlvdSB3YW50IHRvIGtlZXAgbWFzdG9kb24uc29jaWFsLgoKYGBge3IgbWVzc2FnZSA9IEZBTFNFLCB3YXJuaW5nID0gRkFMU0V9Cm1haW5faW5zdGFuY2VzIDwtIG1hc3RvZG9uICU+JQogIGdyb3VwX2J5KGFjY291bnRfaW5zdGFuY2UpICU+JQogIHN1bW1hcmlzZSh0b290c19pbnN0YW5jZSA9IG4oKSkgJT4lCiAgZmlsdGVyKGFjY291bnRfaW5zdGFuY2UgIT0gIm1hc3RvZG9uLnNvY2lhbCIpICU+JQogIHRvcF9uKDYpICU+JQogIGFycmFuZ2UoZGVzYyh0b290c19pbnN0YW5jZSkpCgptYWluX2luc3RhbmNlcwpgYGAKCk5vdyBvdXIgZ3JhcGggaW5jbHVkZXMgdGhlIGZvbGxvd2luZyBpbnN0YW5jZXMgOiBgciBwYXN0ZShtYWluX2luc3RhbmNlcyRhY2NvdW50X2luc3RhbmNlLCBzZXA9IiwgIilgLiBFdmVuIHRob3VnaCB0aGUgZ3JhcGggbGluZXMgYXJlIHJhdGhlciBpbnRlcnR3aW5lcyB3aXRoIG91ciBkZWZhdWx0IHNldHRpbmdzLCB0aGUgZGF5L25pZ2h0IGNvbnRyYXN0IHRlbmRzIGNsZWFybHkgdG8gdmFyeSBhY2Nyb3NzIHRoZSBpbnN0YW5jZXMuIFRoYXQgbWF5IHN1Z2dlc3QgdGhhdCBzb21lIGluc3RhbmNlcyBhcmUgbW9yZSAibG9jYWxpemVkIiBvciBtb3JlICJpbnRlcm5hdGlvbmFsIiB0aGFuIG90aGVycy4KCmBgYHtyfQptYXN0b2RvbiAlPiUKICBmaWx0ZXIoY3JlYXRlZF9hdF9ob3VyICE9IGNyZWF0ZWRfYXRfaG91clt3aGljaC5taW4oY3JlYXRlZF9hdF9ob3VyKV0pICU+JQogIGZpbHRlcihjcmVhdGVkX2F0X2hvdXIgIT0gY3JlYXRlZF9hdF9ob3VyW3doaWNoLm1heChjcmVhdGVkX2F0X2hvdXIpXSkgJT4lCiAgZmlsdGVyKGFjY291bnRfaW5zdGFuY2UgJWluJSBtYWluX2luc3RhbmNlcyRhY2NvdW50X2luc3RhbmNlKSAlPiUKICBncm91cF9ieShjcmVhdGVkX2F0X2hvdXIsIGFjY291bnRfaW5zdGFuY2UpICU+JSAKICBzdW1tYXJpc2Uobl9ob3VyPW4oKSkgJT4lCiAgZ2dwbG90KGFlcyhjcmVhdGVkX2F0X2hvdXIsIG5faG91ciwgY29sb3I9YWNjb3VudF9pbnN0YW5jZSkpICsgCiAgZ2VvbV9saW5lKHNpemU9MSkgKwogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbj0iYm90dG9tIikgKwogIGxhYnMoeD0iSG91ciIsIHk9Ik51bWJlciBvZiB0b290cyIsIGNvbG9yPSJJbnN0YW5jZSIsIHRpdGxlPSJQYXJ0aWNpcGF0aW9uIGJ5IGluc3RhbmNlcyIsIGNhcHRpb249IlNvdXJjZSA6IFB1YmxpYyB0aW1lbGluZSBvZiBtYXN0b2Rvbi5zb2NpYWwiKQpgYGAKClRvIGhhdmUgYSBxdWljayBpZGVhIG9mIGhvdyBtdWNoIGluc3RhbmNlcyBhcmUgYWZmZWN0ZWQgYnkgdGhlIEdNVCBuaWdodC1kYXkgc2hpZnQsIHdlIGNhbiBjaGVjayB0aGUgZW50aHJvcHkgdmFsdWUuIFRpbWUgc2VyaWVzIGRlY29tcG9zaXRpb24gd291bGQgYmUgbW9yZSByZWxldmFudCBidXQgc2luY2Ugd2Ugc3RpbGwgaGF2ZSBsaXR0bGUgZGF0YSBhdmFpYWJsZSwgYSByb3VnaCBhcHByb3hpbWF0aW9uIG9mIGhvdyBtdWNoIHRoZSB0ZW5kZW5jeSBkZXZpYXRlcyBmcm9tIHVuaWZvcm0gZGlzdHJpYnV0aW9uIHNoYWxsIGRvIHRoZSB0cmljay4gVGhlICJkbyIgZnVuY3Rpb24gc3RhdGVkIGJlbG93IGlzIGEgcHJlY2lvdXMgYWRkaXRpb24gdG8gUiB0aGF0IGFsbG93cyB0byBhcHBseSBhbnkgZnVuY3Rpb24gdG8gZWFjaCBncm91cCBvZiBkYXRhLgoKYGBge3J9CmxpYnJhcnkoZW50cm9weSkKCm1hc3RvZG9uX2VudHJvcHkgPC0gbWFzdG9kb24gJT4lCiAgZmlsdGVyKGNyZWF0ZWRfYXRfaG91ciAhPSBjcmVhdGVkX2F0X2hvdXJbd2hpY2gubWluKGNyZWF0ZWRfYXRfaG91cildKSAlPiUKICBmaWx0ZXIoY3JlYXRlZF9hdF9ob3VyICE9IGNyZWF0ZWRfYXRfaG91clt3aGljaC5tYXgoY3JlYXRlZF9hdF9ob3VyKV0pICU+JQogIGZpbHRlcihhY2NvdW50X2luc3RhbmNlICVpbiUgbWFpbl9pbnN0YW5jZXMkYWNjb3VudF9pbnN0YW5jZSkgJT4lCiAgZ3JvdXBfYnkoY3JlYXRlZF9hdF9ob3VyLCBhY2NvdW50X2luc3RhbmNlKSAlPiUgCiAgc3VtbWFyaXNlKG5faG91cj1uKCkpICU+JQogIHVuZ3JvdXAoKSAlPiUKICBncm91cF9ieShhY2NvdW50X2luc3RhbmNlKSAlPiUKICBkbyhlbnRyb3B5X3ZhbHVlID0gZW50cm9weSguJG5faG91ciwgbWV0aG9kPSJNTSIpKSAlPiUKICB1bm5lc3QoZW50cm9weV92YWx1ZSkKCmdncGxvdChtYXN0b2Rvbl9lbnRyb3B5LCBhZXMoYWNjb3VudF9pbnN0YW5jZSwgZW50cm9weV92YWx1ZSwgc2l6ZT0xLCBjb2xvcj1hY2NvdW50X2luc3RhbmNlKSkgKyAKICBnZW9tX3BvaW50KCkgKwogIHlsaW0oMy45LCA0LjIpICsKICBndWlkZXMoc2l6ZT1GQUxTRSwgY29sb3I9RkFMU0UpICsKICBsYWJzKHg9Ikluc3RhbmNlIiwgeT0iRW50cm9weSB2YWx1ZSIsIGNvbG9yPSJJbnN0YW5jZSIsIHRpdGxlPSJFbnRyb3B5IHZhbHVlIGFjY3Jvc3MgdGhlIGhvdXJzIGJ5IGluc3RhbmNlcyIsIGNhcHRpb249IlNvdXJjZSA6IFB1YmxpYyB0aW1lbGluZSBvZiBtYXN0b2Rvbi5zb2NpYWwiKQpgYGAKCkhlcmUgdGhlIHR3byBsb3dlc3QgdmFsdWUgKHRoYXQgaXMgdGhhdCBkZXZpYXRlcyB0aGUgbW9zdCBmcm9tIHRoZSB1bmlmb3JtIGRpc3RyaWJ1dGlvbikgYXJlIGF0dGFpbmVkIGJ5IHR3byBpbnN0YW5jZXMgY3JlYXRlZCBpbiBHZXJtYW55LCBpY29zYWhlZHJvbi53ZWIgYW5kIHNvY2lhbC50Y2huY3MuZGUsIHdoZXJlYXMgbWFzdG9kb24uY2xvdWQgYW5kIG9jdG9kb24uc29jaWFsICh3aGljaCBjbGFpbXMgdG8gImZlZGVyYXRlIGV2ZXJ5d2hlcmUiKSBzZWVtcyBtb3JlIGxpa2VseSB0byBhdHRyYWN0IG5vbi1ldXJvcGVhbiB1c2Vycy4KCiMjIEhvdyBkbyBpbnN0YW5jZXMgcmVsYXRlcyB0byBlYWNoIG90aGVyPyA8YnI+IEEgbmV0d29yayBvZiBub3RpZmljYXRpb25zLgoKTm93IGl0J3MgdGltZSBmb3IgdGhlIHRyaWNreSBwYXJ0OiB0cnlpbmcgdG8gbWFwIHRoZSBuZXR3b3JrIGFz4oCmIGEgbmV0d29yay4gQXMgdHlwaWNhbCBvZiBhbnkgc29jaWFsIG5ldHdvcmssIHRvb3RzIGFyZSBub3QgaXNvbGF0ZWQgcHVibGljYXRpb25zLCBidXQgYXJlICJjb25uZWN0ZWQiIHRocm91Z2ggYSB3aWRlIGFycmF5IG9mIGludGVyYWN0aW9ucy4gQW5kLCBsZXNzIHR5cGljYWxseSwgdGhlc2VzIGludGVyYWN0aW9ucyBhcmUgcGFydCBvZiB3aWRlciBpbnRlcnJlbGF0aW9uIGJldHdlZW4gdGhlICJpbnN0YW5jZXMiIGFuZCBjYW4gaGVscCB1cyB0byBjaGVjayB0aGVpciBpbm5lciAiY29tbXVuaXR5IGNvaGVyZW5jZXMiOiB0aGUgbGVzcyBhbiBpbnN0YW5jZSBpcyBsaW5rZWQgdG8gb3RoZXIgaW5zdGFuY2VzLCB0aGUgbW9yZSBsaWtlbHkgaXQgc2VlbXMgdG8gYmUgY29tbXVuaXR5LWNlbnRyaWMuCgpUaGUgIm1lbnRpb25zIiBhcmUgdGhlIG1vc3QgaW50ZXJlc3RpbmcgZGF0YSBpbW1lZGlhdGVseSBhdmFpbGFibGUgdG8gZ2V0IGJ1aWxkIGEgbmV0d29yayBtb2RlbC4gWWV0LCB0aGV5IGFyZSBub3QgcHJlc2VudGVkIGluIGEgY3VzdG9tIGZvcm1hdCwgYnV0IGFzICJsaXN0cyIuIEEgcXVpY2sgaW5zcGVjdGlvbiBvbiBvdXIgZGF0YWZyYW1lIGNvbmZpcm1zIHRoYXQgd2UgaGF2ZSB0byBkZWFsIHdpdGggYSBjb21tYS1zZXBhcmF0ZWQgY29tYmluYXRpb24gb2YgYW4gdW5rbm93biBudW1iZXIgb2YgYWNjb3VudHMuCgpgYGB7cn0KbGlicmFyeShzdHJpbmdyKQptYXN0b2RvbiAlPiUKICBzZWxlY3QodG9vdF9tZW50aW9uc19hY2NvdW50KSAlPiUKICBmaWx0ZXIoc3RyX2RldGVjdCh0b290X21lbnRpb25zX2FjY291bnQsICIsIikpCmBgYApCZWZvcmUgd2UgY2FuIGdldCBhbnkgZnVydGhlciwgd2UgaGF2ZSB0byAidW5uZXN0IiBvdXIgZGF0YWZyYW1lIHNvIHRoYXQgZWFjaCByb3cgbWFwcyBhIHNwZWNpZmljIG1lbnRpb24gKGFsbCB0aGUgcmVsYXRlZCBtZXRhZGF0YSBiZWluZyBjb25zZXF1ZW50bHkgcmVwZWF0ZWQpLiBUaGUgaWQgYW5kIHRoZSBuYW1lIG9mIHRoZSBhY2NvdW50cyB0YXJnZXRlZCBieSB0aGUgbWVudGlvbnMgYXJlICJzcGxpdCIgaW50byBhIGxpc3QgKHdpdGggc3Ryc3RwbGl0KSBhbmQgdGhlIHdob2xlIGRhdGFzZXQgaXMgInVubmVzdGVkIiBhY2NvcmRpbmcgdG8gdGhlIGxpc3Qgb2YgbWVudGlvbnMuIFRoZW4gd2UgInNlcGFyYXRlIiB0aGUgYWNjb3VudCBhbmQgaW5zdGFuY2VzIG9mIGVhY2ggbWVudGlvbnMgKGFzIHdlIGRpZCBvbiB0aGUgbmFtZXMgb2YgdGhlIGFjY291bnQgZWFybGllciBvbikuIAoKYGBge3IgbWVzc2FnZSA9IEZBTFNFLCB3YXJuaW5nID0gRkFMU0V9Cm1hc3RvZG9uX3JlcGxpZXMgPC0gbWFzdG9kb24gJT4lIAogIHNlbGVjdCh0b290X21lbnRpb25zX2FjY291bnQsIGNyZWF0ZWRfYXQsIGFjY291bnRfbmFtZSwgYWNjb3VudF9pbnN0YW5jZSkgJT4lIAogIGZpbHRlcih0b290X21lbnRpb25zX2FjY291bnQgIT0gIk5vbmUiKSAlPiUgCiAgbXV0YXRlKHRvb3RfbWVudGlvbnNfYWNjb3VudCA9IHN0cnNwbGl0KHRvb3RfbWVudGlvbnNfYWNjb3VudCwgIiwgIikpICU+JSAKICB1bm5lc3QodG9vdF9tZW50aW9uc19hY2NvdW50KQoKbWFzdG9kb25fcmVwbGllcyA8LSBtYXN0b2Rvbl9yZXBsaWVzICU+JQogIHNlcGFyYXRlKHRvb3RfbWVudGlvbnNfYWNjb3VudCwgYygibWVudGlvbl9uYW1lIiwgIm1lbnRpb25faW5zdGFuY2UiKSwgc2VwPSJAIikgJT4lIAogIG11dGF0ZShtZW50aW9uX2luc3RhbmNlID0gaWZlbHNlKGlzLm5hKG1lbnRpb25faW5zdGFuY2UpLCAibWFzdG9kb24uc29jaWFsIiwgbWVudGlvbl9pbnN0YW5jZSkpCgptYXN0b2Rvbl9yZXBsaWVzCmBgYAoKRXZlcnl0aGluZyBpcyBub3cgbWFwZWQgYXJvdW5kIHRoZSAibWVudGlvbiIgdW5pdCA6IGVhY2ggcm93IGlzIGEgbWVudGlvbiBhbmQgd2hlbiB0aGVpciBpcyBzZXZlcmFsIG1lbnRpb25zLCB0aGUgYWNjb3VudCBuYW1lLCBpbnN0YW5jZXMsIGlkLCBldGMuIGFyZSByZXBlYXRlZCBhY2NvcmRpbmdseS4KClNpbmNlIHRoZXJlIGlzICphIGxvdCogb2YgaW5zdGFuY2VzIChgciBubGV2ZWxzKGFzLmZhY3RvcihtYXN0b2Rvbl9yZXBsaWVzJGFjY291bnRfaW5zdGFuY2UpKWAgb24gdGhlIGFjY291bnQgc2lkZSksIHdlIG9ubHkga2VlcHMgdGhlIG1haW4gb25lLiBIZXJlIG91ciBjYWxjdWxhdGlvbiBpcyBhIGJpdCBtb3JlIGNvbXBsaWNhdGVkIHRoYW4gd2hlbiB3ZSB0cmllZCB0byBkZWZpbmUgbWFpbl9pbnN0YW5jZXM6IHdlIHRyeSB0byBnZXQgdGhlIGdsb2JhbCBjb3VudCBvZiBpbnN0YW5jZXMgb24gYm90aCBzaWRlIG9mIHRoZSBhY2NvdW50LW1lbnRpb24gcmVsYXRpb25zaGlwLCBzbyB0aGF0IHdlIGhhdmUgdG8gbWVyZ2UgYWNjb3VudCBhbmQgbWVudGlvbiBpbnN0YW5jZXMgaW50byBhbiB1bmlmaWVkICJpbnN0YW5jZSIgdmFyaWFibGUuIFdpdGggYSBuZXR3b3JrIHZpc3VhbGlzYXRpb24gYSBoaWdoZXIgdGhyZXNob2xkIGlzIGFjY2VwdGFibGUgOiB3ZSBhcmUgc2V0dGxpbmcgZm9yIDIwLiAibWFzdG9kb24uc29jaWFsIiBpcyBub3QgZmlsZXRlcmVkIGVpdGhlciwgYXMgaXQgd2lsbCBiZSBsZXNzIG92ZXJ3aGVsbWluZy4KCmBgYHtyIG1lc3NhZ2UgPSBGQUxTRSwgd2FybmluZyA9IEZBTFNFfQptYWluX3JlcGx5X2luc3RhbmNlcyA8LSBkYXRhX2ZyYW1lKGMobWFzdG9kb25fcmVwbGllcyRhY2NvdW50X2luc3RhbmNlLCBtYXN0b2Rvbl9yZXBsaWVzJG1lbnRpb25faW5zdGFuY2UpKSAlPiUKICBzZXROYW1lcyguLCBjKCJpbnN0YW5jZSIpKSAlPiUKICBncm91cF9ieShpbnN0YW5jZSkgJT4lCiAgc3VtbWFyaXNlKGFjY291bnRfbWVudGlvbnMgPSBuKCkpICU+JQogIHRvcF9uKDIwKSAlPiUKICBhcnJhbmdlKGRlc2MoYWNjb3VudF9tZW50aW9ucykpCgptYWluX3JlcGx5X2luc3RhbmNlcwpgYGAKCk5vdyB3ZSBvbmx5IGtlZXAgaW4gbWFzdG9kb25fcmVwbGllcyB0aGUgaW5zdGFuY2VzIHJlZ2lzdGVyZWQgaW4gbWFpbl9yZXBseV9pbnN0YW5jZXMuIFRoZW4gd2UgcmVhbGx5IGdldCBpbnRvIHRoZSBuZXR3b3JrIGJ1c2luZXNzICEgV2UgbG9hZCB0aGUgaWdyYXBoIFIgKHRvIHN1bSBpdCB1cCBxdWlja2x5LCBsZXQncyBzYXkgdGhhdCdzIHRoZSBSIGNvdW50ZXJwYXJ0IG9mIEdlcGhpKSBhbmQgd2UgdHJhbnNmb3JtIG91ciBkYXRhIHNldCBpbnRvIGEgZ3JhcGggb2YgbmV0d29ya2VkIGludGVyYWN0aW9uLgoKYGBge3IgbWVzc2FnZSA9IEZBTFNFLCB3YXJuaW5nID0gRkFMU0V9CmxpYnJhcnkoaWdyYXBoKQptYXN0b2Rvbl9uZXR3b3JrIDwtIG1hc3RvZG9uX3JlcGxpZXMgJT4lCiAgZmlsdGVyKGFjY291bnRfaW5zdGFuY2UgJWluJSBtYWluX3JlcGx5X2luc3RhbmNlcyRpbnN0YW5jZSkgJT4lCiAgZmlsdGVyKG1lbnRpb25faW5zdGFuY2UgJWluJSBtYWluX3JlcGx5X2luc3RhbmNlcyRpbnN0YW5jZSkgJT4lCiAgc2VsZWN0KGFjY291bnRfaW5zdGFuY2UsIG1lbnRpb25faW5zdGFuY2UpICU+JQogIGdyYXBoX2Zyb21fZGF0YV9mcmFtZSgpCgptYXN0b2Rvbl9uZXR3b3JrCmBgYAoKQW5kIGEgbGFzdCByb3VuZCBvciAoY29tcGxpY2F0ZWQpIGNhbGN1bGF0aW9uLiBXZSB0cnkgdG8gc2ltcGxpZnkgdGhlIG5ldHdvcmsgKGJ5IG9ubHkgcmVtb3ZpbmcgdGhlIHNlbGYgaW50ZXJhY3Rpb25zIGFuZCByZW1vdmluZyB0aGUgbGVzcyBpbnRlcmVzdGluZyBvbmUpLiBBbmQgc2luY2UgdGhlIG5ldHdvcmsgcmVtYWlucyByYXRoZXIgY29uY2VudHJhdGVkLCB3ZSBkZWZpbmUgdGhlIHNpZGVzIG9mIHRoZSBub2RlIG9uIGEgbG9nYXJpdGhtaWMgc2NhbGUuCgpgYGB7cn0KRShtYXN0b2Rvbl9uZXR3b3JrKSR3ZWlnaHQgPC0gMQptYXN0b2Rvbl9uZXR3b3JrIDwtIHNpbXBsaWZ5KG1hc3RvZG9uX25ldHdvcmssIGVkZ2UuYXR0ci5jb21iPWxpc3Qod2VpZ2h0ID0gInN1bSIsIHRyYW5zYWN0aW9uX2Ftb3VudCA9ICJzdW0iLCBmdW5jdGlvbih4KWxlbmd0aCh4KSkpClYobWFzdG9kb25fbmV0d29yaykkc2l6ZSA8LSAgMTAqbG9nMTAoZGVncmVlKG1hc3RvZG9uX25ldHdvcmspKQoKcGxvdChtYXN0b2Rvbl9uZXR3b3JrLCBlZGdlLmFycm93LnNpemU9MC41LCBsYXlvdXQ9bGF5b3V0LmZydWNodGVybWFuLnJlaW5nb2xkLCBtYXJnaW49LTAuMSkKYGBgCgpXaGlsZSB3ZSBjbGVhcmx5IHNlZSB0aGF0IHNvbWUgaW5zdGFuY2VzIGFyZSBtb3JlICJjb25uZWN0ZWQiIHRoYW4gb3RoZXJzLCBpdCBpcyBkaWZmaWN1bHQgdG8gbWFrZSBvdXQgd2hhdCBoYXBwZW5zIHdpdGhpbiB0aGUgY29yZSBvZiB0aGUgbmV0d29ya3MuIE9ubHkgb25lIG1lbnRpb24gaXMgZW5vdWdoIHRvIGNyZWF0ZSBhIGxpbmsgYmV0d2VlbiB0d28gaW5zdGFuY2VzLiBBIGdvb2Qgd2F5IHRvIGNsYXJpZnkgdGhlc2VzIHJlbGF0aW9uc2hpcHMgaXMgdG8gc2V0IGEgaGlnaGVyIHRocmVzaG9sZCBmb3IgZGlzcGxheWluZyBhIGxpbmsuIEhlcmUgd2UgaGF2ZSBmaW5hbGx5IG9wdGVkIGZvciBhIG1pbmltYWwgd2VpZ2h0IG9mIDQgYnV0IGFueSBvdGhlciBudW1iZXIgY2FuIGJlIHNldCB1cCB0byBtaW5fd2VpZ2h0LgoKYGBge3J9Cm1pbl93ZWlnaHQgPSA0Cm1hc3RvZG9uX25ldHdvcmtfMj1kZWxldGUuZWRnZXMobWFzdG9kb25fbmV0d29yaywgd2hpY2goRShtYXN0b2Rvbl9uZXR3b3JrKSR3ZWlnaHQgPD1taW5fd2VpZ2h0KSkKbWFzdG9kb25fbmV0d29ya18yID0gZGVsZXRlX3ZlcnRpY2VzKG1hc3RvZG9uX25ldHdvcmtfMiwgIm1hc3RvZG9uLnNvY2lhbCIpCmlzb2xhdGVzIDwtIHdoaWNoKGRlZ3JlZShtYXN0b2Rvbl9uZXR3b3JrXzIsIG1vZGUgPSAiYWxsIikgPT0gMCkKbWFzdG9kb25fbmV0d29ya18yIDwtIGRlbGV0ZS52ZXJ0aWNlcyhtYXN0b2Rvbl9uZXR3b3JrXzIsIGlzb2xhdGVzKQpFKG1hc3RvZG9uX25ldHdvcmtfMikkY29sb3IgPSByZ2IoMSwgLjgsIC44KQpwbG90KG1hc3RvZG9uX25ldHdvcmtfMiwgZWRnZS5hcnJvdy5zaXplPTAuNSwgbGF5b3V0PWxheW91dC5mcnVjaHRlcm1hbi5yZWluZ29sZCwgZWRnZS53aWR0aD1FKG1hc3RvZG9uX25ldHdvcmtfMikkd2VpZ2h0KQpgYGAKClRoZSBuZXR3b3JrIHNlZW1zIHRvIHdvcmsgaW4gY29uY2VudHJpYyBjaXJjbGVzLCB3aXRoIG1vcmUgaW50ZWdyYXRlZCBpbnN0YW5jZXMgY29tbXVuaWNhdGluZyBleGNsdXNpdmVseSB3aXRoIG1vcmUgcGVyaXBoZXJhbCBpbnN0YW5jZXMgKHN1Y2ggYXMgdGhlIGluc3RhbmNlIG9mIHRoZSAqUXVhZHJhdHVyZSBkdSBuZXQqLCBtYW1vdC5mciwgd2l0aCBtYXN0b2Rvbi5nb3VnZXJlLmZyKS4gU28gZmFyLCB0aGUgcHVibGljIHRpbWVsaW5lIGFwcGVhcnMgYXMgdGhlIGNvbW1vbiBmb3J1bSBvZiBzcGVjaWZpYyBuZXR3b3JrcyBvZiBpbnN0YW5jZXMgLS0tIGEgZmVkZXJhdGlvbiBvZiBzbWFsbGVyIGZlZGVyYXRpb25zLgoKIyMgSW5zdGFuY2VzID0gY29tbXVuaXRpZXMgPzxicj5FdmFsdWF0aW5nIGhvbW9nZW5laXR5IHRocm91Z2ggbWVudGlvbnMKClRoZSBuZXR3b3JrIHZpc3VhbGl6YXRpb24gZGlkIG5vdCBkaXNwbGF5IGFuIGltcG9ydGFudCBwaWVjZSBvZiBpbmZvcm1hdGlvbjogdGhlIHNoYXJlIG9mICJzZWxmLW1lbnRpb25zIiB3aXRoaW4gYW4gaW5zdGFuY2UgKGl0IGlzIGFjdHVhbGx5IHBvc3NpYmxlIHRvIGRvIHNvLCBidXQgdGhhdCB3b3VsZCBtYWtlIHRoZSBuZXR3b3JrIG11Y2ggbGVzcyBsZWdpYmxlKS4gVGhhdCBpcyB0aGUgZmFjdCB0aGF0IHRoZSB1c2VycyBvZiBtYXN0b2Rvbi5jbG91ZCBvciBtYW1vdC5mciBtYXkgcmVmZXIgbW9yZSBvciBsZXNzIGV4Y2x1c2l2ZWx5IHRvIHVzZXJzIG9mIHRoZWlyIG93biBpbnN0YW5jZXMgcmF0aGVyIHRoYW4gb3V0c2lkZSBpbnN0YW5jZXMuIFRoaXMgc2VsZi1tZW50aW9uIHJhdGUgY2FuIGJlIGEgc2lnbmlmaWNhbnQgbWVhc3VyZSwgYWxiZWl0IGFwcHJveGltYXRpdmUsIG9mIHRoZSBkZWdyZWUgb2YgY29tbXVuaXR5IGJlbG9uZ2luZyB3aXRoaW4gYW4gaW5zdGFuY2UuCgpTbyBsZXQncyBkZWZpbmUgYSAic2FtZW5lc3MgaW5zdGFuY2UiIHZhcmlhYmxlIHRoYXQgd2lsbCBjb3VudCBhbGwgdGhlIGNhc2Ugd2hlcmUgYWNjb3VudF9pbnN0YW5jZSBpcyB0aGUgc2FtZSBhcyBtZW50aW9uX2luc3RhbmNlLgoKYGBge3J9CnNhbWVuZXNzX2luc3RhbmNlIDwtIG1hc3RvZG9uX3JlcGxpZXMgJT4lCiAgZmlsdGVyKGFjY291bnRfbmFtZSAhPSBtZW50aW9uX25hbWUpICU+JQogIGZpbHRlcihhY2NvdW50X2luc3RhbmNlID09IG1lbnRpb25faW5zdGFuY2UpICU+JQogIHNlbGVjdChpbnN0YW5jZSA9IGFjY291bnRfaW5zdGFuY2UpICU+JQogIGdyb3VwX2J5KGluc3RhbmNlKSAlPiUKICBzdW1tYXJpc2Uoc2FtZV9pbnN0YW5jZSA9IG4oKSkKCnNhbWVuZXNzX3JhdGVfaW5zdGFuY2UgPC0gbWFzdG9kb25fcmVwbGllcyAlPiUKICBmaWx0ZXIoYWNjb3VudF9uYW1lICE9IG1lbnRpb25fbmFtZSkgJT4lCiAgc2VsZWN0KGluc3RhbmNlID0gYWNjb3VudF9pbnN0YW5jZSkgJT4lCiAgZ3JvdXBfYnkoaW5zdGFuY2UpICU+JQogIHN1bW1hcmlzZSh0b3RhbF9pbnN0YW5jZSA9IG4oKSkgJT4lCiAgbWVyZ2Uoc2FtZW5lc3NfaW5zdGFuY2UsIGJ5PSJpbnN0YW5jZSIpICU+JQogIGZpbHRlcighaXMubmEoaW5zdGFuY2UpKSAlPiUKICBtdXRhdGUocmF0ZSA9IChzYW1lX2luc3RhbmNlL3RvdGFsX2luc3RhbmNlKSoxMDApICU+JQogIHRvcF9uKDEwLCB0b3RhbF9pbnN0YW5jZSkKCnNhbWVuZXNzX3JhdGVfaW5zdGFuY2UKYGBgCgpHbG9iYWxseSB0aGUgc2FtZW5lc3MgcmF0ZSBpcyByYXRoZXIgaGlnaCwgd2l0aCBhIG1lYW4gb2YgYHIgcm91bmQobWVhbihzYW1lbmVzc19yYXRlX2luc3RhbmNlJHJhdGUpLCAyKWAgZm9yIHRoZSB0ZW4gbWFpbiBpbnN0YW5jZXMgLS0tIHRoYXQgaXMgYHIgcm91bmQobWVhbihzYW1lbmVzc19yYXRlX2luc3RhbmNlJHJhdGUpLCAwKWAlIG9mIGV2ZXJ5IG5vdGlmaWNhdGlvbiBzZW50IGlzIHNlbnQgdG8gYSB1c2VyIG9mIHRoZSBzYW1lIGluc3RhbmNlLiBBcyBpbGx1c3RyYXRlZCBpbiB0aGUgZ3JhcGggYmVsb3csIGl0IHRlbmRzIHRvIGJlIG1vcmUgc2lnbmlmaWNhbnQgaW4gbmV0d29ya3Mgd2hlcmUgdGhlcmUgaXMgYSBoaWdoZXIgbGV2ZWwgb2YgY29tbXVuaXR5IGJlbG9uZ2luZyAoc3VjaCBhcyB3aXRjaGVzLnRvd24pLgoKYGBge3J9CmdncGxvdChzYW1lbmVzc19yYXRlX2luc3RhbmNlLCBhZXMocmVvcmRlcihpbnN0YW5jZSwgLXRvdGFsX2luc3RhbmNlKSwgcmF0ZSwgZmlsbD1pbnN0YW5jZSkpICsKICBnZW9tX2JhcihzdGF0ID0gImlkZW50aXR5IikgKwogIGd1aWRlcyhmaWxsPUZBTFNFKSArCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoc2l6ZT03KSkgKwogIGxhYnMoeD0iSW5zdGFuY2UiLCB5PSJOb3RpZmljYXRpb24gc2hhcmUgd2l0aGluIGFuIGluc3RhbmNlIiwgZmlsbD0iSW5zdGFuY2UiLCB0aXRsZT0iTWVhc3VyaW5nIGNvbW11bml0eSBiZWxvbmdpbmcgd2l0aCBtZW50aW9uIHNoYXJlcyIsIGNhcHRpb249IlNvdXJjZSA6IFB1YmxpYyB0aW1lbGluZSBvZiBNYXN0b2RvbiIpCmBgYAoKT2YgY291cnNlIHRoZSBzYW1lbmVzcyByYXRlIGlzIG5vdCBpbmVydCBhbmQgbWF5IGV2b2x2ZSB0aHJvdWdoIHRpbWU6IHdoZW5ldmVyIHdlJ2xsIGhhdmUgbW9yZSBkYXRhIGF0IG91ciBkaXNwb3NhbCB3ZSdsbCB0cnkgdG8gZGlzcGxheSB0aGVzZXMgZHluYW1pY3MuCgo+PGRpdiBzdHlsZT0idGV4dC1hbGlnbjogcmlnaHQiPipUbyBiZSBjb250aW51ZWQqPC9kaXY+Cg==