Introduction
Hello dear reader!
By this time, you probably know me reasonably well from all the CVs I
have sent you. It seems like those weren’t that interesting to you,
which I totally understand. But at the same time, CV is not really able
to fully communicate how I actually work and whether the things that I’m
able to create could add value to DuckDuckGo. That’s why I have decided
to show you a piece of my work by collecting and analyzing your data
from the tracker-radar project. I know that this might be a little bit
weird and annoying. But, I see working at DuckDuckGo as a great
opportunity - far too great to give up right away. With that being said,
if you decide to reject my application again, after seeing a piece of my
work, I will fully accept that and I won’t reach out again. Thank
you!
– Stanislav “Stano” Smatana
What happend in 2022? Analysis of tracker-radar data in time.
tldr; It seems like something interesting have
happend some time between 2022 and 2023 in the world of website
trackers. The nature of trackers have suddenly shifted and this shift
seems to be connected with fingerprinting based on viewport
characteristics. Do you know what could have happend? If you want to get
right into results, jump to the section Analyzing Suspected
Tracker Patterns in Time. If you want to see how I went over
analyzing this data step by step - continue reading.
I’ve decided to download and analyze website tracking data collected
by DuckDuckGo’s tracker-radar with following goals:
- Characterize potential trackers based on combinations of API calls
they are performing.
- Understand how character of trackers differs across big players and
how it evolved in time.
All of the data I’ve used in the analysis was extracted from domain
json files availiable across all releases of . You can see the
shape of the raw input data below. Each records corresponds to one type
of API call in one resource of one domain, meaning that the overall
grain of the table is release_tag (date), region, domain, resource, api
call. The data contains following columns:
- date - date of data collection, extracted from
release_tag
- region - region in which this record was collected
(corresponds to subfolders of domain folder)
- rule - regex identifying the resource
- api - api function
- calls - number of calls to the api function
- fingerprinting - fingerprinting score derived in
tracker-radar for the resource
library(dplyr)
library(topicmodels)
api_calls_in_time <- read.csv("data/api_calls_by_release.csv") %>%
filter(type == "Script")
#converting release tags to dates
release_tag_to_date <- data.frame(release_tag = unique(api_calls_in_time$release_tag)) %>%
separate(release_tag, into = c("year", "month", "day"), sep = "\\.", fill = "right",remove = F) %>%
mutate(
month = sprintf("%02d", as.integer(month)),
day = ifelse(is.na(day), "01", sprintf("%02d", as.integer(day))),
date = as.Date(paste(year, month, day, sep = "-"))
) %>%
select(release_tag,date)
api_calls_in_time <- api_calls_in_time %>%
inner_join(release_tag_to_date) %>%
select(date,region,domain,rule,api,calls,fingerprinting)
head(api_calls_in_time)
Python scripts I’ve used to scrape tracker-radar together with the
source code of this notebook and other artifacts are availiable here
TODO.
NOTE: Python is my primary language, but I love to use R for
exploratory data analysis, so that’s what I’m using here :)
AI Use During this Analysis
During this process I’ve used AI (GitHub Copilot) at times to
complement my primary skills. I have used AI exactly in this way:
- I have used AI to generate python scripts for
scraping of ,
because I didn’t want to waste time implementing scraping and to focus
on analysis.
- I did not use AI to conduct the analysis of this
data and create this notebook, because that’s the part that I enjoy,
where I excel, and where I do a better job.
- I have used AI to help interpret LDA topics
(details in the next section), because my knowledge of web trackers is
very limited :)
Characterizing API Calls - Latent Dirichlet Allocation
Fingerprinting is connected to certain Javascript API calls that can
help create precise identification of a device or a user. My assumption
is that there are certain types of trackers and that each tracker type
is associated with certain API calls. In order to see what these
suspected tracker types might be, I need first to identify API calls
that tend to occur together - API call groups.
Nearly any clustering approach could be used to create some groups of
API calls, but I have decided to use Latent Dirichlet
Allocation (LDA, not to be confused with Linear Discriminant
Analysis). While this model traditionally comes from the field of
Natural Language Processing, its assumptions exactly match this setting
- sparse count data (counts of api calls with only few appearing in each
resource). Don’t be scared by the complicated name, it’s actually a
fairly simple unsupervised generative model. Let me explain how it
specifically works for this analysis.
Every statistical model assumes certain view of the world and then
helps us derive new information based on this view. In this setting, LDA
assumes that certain API calls tend to occur together. These co-ocuring
calls can be grouped into larger call groups. Each call can be a member
of multiple groups and this membership is soft. This means that each
call is associated with each group (topic in LDA language) with certain
probability between 0 and 1.
With groups established, script resources (documents in LDA language)
are viewed as different mixes of groups. Each mix is characterized by
resource to group associations between 0-1 that add up to 1. Groups,
associations between resources and groups and associations between
groups and calls are derived by LDA from data without supervision. The
only required parameter is the number of groups (topics). I will
describe selection of the number of groups later.
The diagram below illustrates how LDA models an example of 2 script
resources. Each script is associated with call groups 1-4 and its group
mix dictates the actual calls that appear in each script resource. For
instance, example1.js is 70% group1 and 70% group2. Therefore, its API
calls come mostly from group2 and then also from group1. This is a toy
example, in real LDA, each document is usually associated with each
topic to an extent.
What does LDA enable me to do?
- Reduce dimensionality in an interpretable way. Instead of
characterizing resources by all api call types, I can characterize them
by whole call groups.
- Understand which API calls tend to co-occur and understand what what
these groups of co-occuring calls might represent.

In order to derive call groups I will use only data from the most
recent release. Afterwards, I’ll apply the model to the full data
spanning all tracker-radar releases. I do this because I assume that
today we have the broadest spectrum of tracking techniques available and
the past was equally or less diverse.
api_calls_recent <-
api_calls_in_time %>%
filter(date == max(api_calls_in_time$date))
head(api_calls_recent)
One last thing I need to do in order to apply LDA is to determine the
number of call groups (topics) - k. There are many methods of doing
that. I have decided to use one based on coisne similarity (Cao et.
al. 2009), which aims to create groups that are as distinct from one
another as possible. This is helpful because LDA sometimes tends to
create groups (topics) that are heavily overlapping. Presence of
multiple overlapping groups that correspond roughly to the same concept
creates noise that makes interpretation harder.
More formally, k, for which the model has groups with smallest
average pairwise cosine similarity is selected as the winning k.
cosine_sim <- function(M) {
# row norms
norms <- sqrt(rowSums(M * M))
# denominator via outer
den <- outer(norms, norms, "*")
# dot products
dot <- M %*% t(M)
# cosine similarity
cos_sim <- dot / den
cos_sim
}
avg_cos_sim <- function(M){
csim <- cosine_sim(M)
mean(csim[upper.tri(csim)])
}
call_counts <- api_calls_recent %>%
pivot_wider(names_from=api,values_from = calls,values_fill = 0)
call_counts_mat <- call_counts %>%
select(-c(region,domain,rule,fingerprinting,date)) %>%
as.matrix()
#This takes a lot of time, just load the rds if winner was already calculated
if(file.exists("data/winning_lda.Rds")){
winning_lda = readRDS("data/winning_lda.Rds")
}else
{
csims <- rep(0,times=19)
for(k in 2:20){
cat(paste("Processing LDA ",k,"\n"))
topic_model <- LDA(call_counts_mat,k)
csims[k-1] <- avg_cos_sim(topic_model@beta)
}
selected_k <- (2:20)[which.min(csims)]
winning_lda = LDA(call_counts_mat,selected_k)
}
selected_k = ncol(winning_lda@gamma)
LDA with 4 topics was selected as the model with the lowest average
cosine similarity between API call groups (topics). Next, I want to
characterize each group (topic) and for that I’ll identify calls that
are most characteristic for each group. This can be done by looking into
the beta matrix that has 4 rows, one for each group, and as many columns
as there are distinct call types. Each cell corresponds to probability
(in log scale) of corresponding call being associated with a
corresponding call group. Small excerpt of the matrix below:
assoc_mat <- winning_lda@beta[,1:3]
colnames(assoc_mat) <- winning_lda@terms[1:3]
rownames(assoc_mat) <- paste("Group",1:4)
assoc_mat
HTMLCanvasElement.prototype.toDataURL Navigator.prototype.hardwareConcurrency Navigator.prototype.plugins
Group 1 -46.927331 -39.063618 -27.901858
Group 2 -9.249582 -25.311132 -20.326818
Group 3 -31.002229 -12.835772 -31.452065
Group 4 -5.318555 -3.823308 -3.639734
However, it is important to note that if a call has high probability
of appearing in a given group, it doesn’t automatically mean that it is
very characteristic of this group. It might be the case that it is a
very common call that has equally high probability of appearing in each
group.
Luckily, it is possible to use Bayes rule to calculate the
probability of call being characteristic of a given
group. This is done simply by normalizing the columns of the beta matrix
into 0-1 range. I have used logsumexp and subtraction before applying
exp. The same result could be achieved by exponentiating first and then
normalizing using division by sum of probabilities. However,
calculations in log scale are numerically more stable, so it makes sense
to stay in the log scale as long as possible.
Next, I identify the most characteristic API calls for each group -
API calls that have probability of being characteristic of a given group
higher than 80%.
characteristic_terms <- function(lda_model){
sums <- apply(lda_model@beta,2,logSumExp)
p_distinctive <- exp(sweep(lda_model@beta,2,sums))
n_topics <- nrow(lda_model@beta)
results <- list()
for(i in 1:n_topics){
ordering <- order(lda_model@beta[i,],decreasing = T)
ordered_terms <- lda_model@terms[ordering]
ordered_selection <- (distinctive[i,] > 0.8)[ordering]
selected_ordered_terms <- ordered_terms[ordered_selection]
ordered_ps <- (exp(lda_model@beta[i,])[ordering])[ordered_selection]
names(ordered_ps) <- selected_ordered_terms
results[[i]] <- ordered_ps
}
results
}
characteristic_calls <- characteristic_terms(winning_lda)
Because my knowledge of web tracking methods is fairly limited, I
have consulted AI at this stage. For each group, I have taken the
characteristic API calls and asked AI (Copilot) these questions:
- “What these javascript function calls have in common?”
- “How would you characterize this group?”
- “What name would you give to this group of functions?”
I have made sure to ask these questions without mentioning web
tracking. Interestingly, for each group, the AI have immediately
identified that tracking/fingerprinting might be at play. Answers helped
me establish names and characterization of these API call groups.
The series of barplots below shows characteristic calls for each call
group. The length of the bar corresponds to how frequently is given call
associated with a given group.
Identity & Persistance API Call Group
Functions focused on persistante storage and high-entropy
identifiers.

Browser Personality API Call Group
Calls focused on characterizing user’s browser. This is a broad group
- limiting to top 15 most common calls.

Rendering & Viewing Surface API Call Group
Mostly properties of the viewport, rendering quirks and motion
sensor.

Capability & Hardware API Call Group
Mostly functions that can be used to get broad information on
hardware and device capabilities. Big group, showing only top 15.

API Call Groups and Suspected Tracking
Application of LDA above have helped to identify certain groups of
calls that tend to co-occur. This does not mean, however, that all of
them are indication of fingerprinting. In order to see which groups or
group combinations might be associated with fingerprinting, I’m using
logistic regression on resource-group associations, with outcome
(predicted) variable being the fingerprinting score calculated by
tracker-radar. I want to see which call groups, or their combinations
are associated with high fingerprinting score.
Of course, the fingerprinting score itself is just a heuristic and
might not be always correct. But this is ok for initial exploratory
analysis to discover interesting hypotheses. To make things easier for
interpretation, I have made two simplifications:
- I have binarized associations between call groups and script
resources. Call group is associated with a given script resource, if its
association probability with given group is greater than what would be
expected in 70% of cases.
- Instead of predicting fingerprinting score of 1 - 3, I’m predicting
binary outcome of fingerprinting score being 1 vs 2 or 3.
Remember that I’m not using classifier here to accurately predict the
fingerprinting score. The main purpose is to see which call groups or
call group combinations might be associated with high fingerprinting
scores and therefore likely associated with trackers/fingerprinting. I’m
also not doing the more common train-test-validation split, but using
information criterion (AIC) for model selection. I’m not interested in
how exactly this model would work in production - I’m just interested in
choosing better model.
You can see the excerpt of data used for training below:
assoc_thresholds <- colQuantiles(lda_winner@gamma,probs = 0.7)
binary_activations <- lda_winner@gamma > assoc_thresholds
binary_activations_df <- as.data.frame(activations)
binary_activations_df$outcome <- call_counts$fingerprinting > 1
head(binary_activations_df)
I have tried 2 versions of logistic regression:
- Regressing on each variable, but not on their combinations
- Regression on each variable and each pairwise variable combination
(pair interactions)
The code below fits these models and outputs their AIC score (lower
is better).
pred_model_base <- glm(outcome ~ V1 + V2 + V3 + V4,binary_activations_df,family = "binomial")
pred_model_pairs <- glm(outcome ~ V1 + V2 + V3 + V4 + V1*V2 + V1*V3 + V1*V4 + V2*V3 + V2*V4 + V3*V4,binary_activations_df,family = "binomial")
data.frame(model=c("Baseline Model","Interaction Model"),AIC = c(AIC(pred_model_base),
AIC(pred_model_pairs)))
Interaction model is a clear winner. The difference in AIC value
might seem small at the first sight. However, AIC is defined on a
logarithmic scale, so this difference is actually very significant.
Next, I have generated all possible combinations of activations to see
which will be predicted by the model to have more than 50% probability
of tracking behavior.
The table below shows the call group combinations associated with
tracking/fingerprinting behavior (p>50%) ordered from most probable
to the least probable. Acronym for each combination was created by
taking first letters of each call group taht it consists of.
all_combs <- expand.grid(V1=c(T,F),V2=c(T,F),V3=c(T,F), V4 = c(T,F))
all_combs$Probability <- predict(pred_model_pairs,all_combs,type="response")
all_combs %>%
filter(Probability > 0.5) %>%
arrange(desc(Probability)) %>%
mutate(
Acronym = paste0(ifelse(V1,"I",""),ifelse(V2,"BP",""),ifelse(V3,"R",""),ifelse(V4,"C","")),
V1 = ifelse(V1,"Identity&Persistance",""),
V2 = ifelse(V2,"Browser Personality",""),
V3 = ifelse(V3,"Rendering&Viewport",""),
V4 = ifelse(V4,"Capability","")
) %>%
select(Acronym,V1,V2,V3,V4,Probability)
Analyzing Suspected Tracker Patterns in Time
Having established the API call groups and their combinations
associated with fingerprinting, I can now use these combinations to see
how the character of fingerprinting evolved in time. First, I’ll
investigate overall development and then I’ll investigate the biggest
players in tracking.
In order to do that, I had to do some data processing focused on
annotation of each script resource by the call group combination it
belongs to.
call_counts_in_time <-api_calls_in_time %>%
pivot_wider(names_from=api,values_from = calls,values_fill = 0)
call_counts_mat_in_time <- call_counts_in_time %>%
select(-c(date,region,domain,rule,fingerprinting)) %>%
as.matrix()
#Order columns in the same way as in LDA training
call_counts_mat_in_time <- call_counts_mat_in_time[,colnames(call_counts_mat)]
non_zero_row <- rowSums(call_counts_mat_in_time) > 0
call_counts_mat_in_time <- call_counts_mat_in_time[non_zero_row,]
call_counts_in_time_nonzero <- call_counts_in_time[non_zero_row,]
topic_posteriors <- posterior(lda_winner,call_counts_mat_in_time)
activations_in_time <- topic_posteriors$topics > assoc_thresholds
colnames(activations_in_time) <- c("V1","V2","V3","V4")
tracker_types_in_time <- activations_in_time %>% as.data.frame %>%
mutate(
tracker_type = case_when(
V1 & V2 & V3 & V4 ~ "IBPRC", # Identity + Browser Personality + Rendering + Capability
V2 & V3 & V4 ~ "BPRC", # Browser Personality + Rendering + Capability
V2 & V4 ~ "BPC", # Browser Personality + Capability
V3 & V4 ~ "RC", # Rendering + Capability
V4 ~ "C", # Capability only
V1 & V2 & V3 ~ "IBPR", # Identity + Browser Personality + Rendering
V1 & V2 & V4 ~ "IBPC", # Identity + Browser Personality + Capability
V1 & V3 & V4 ~ "IRC", # Identity + Rendering + Capability
TRUE ~ NA_character_
)
)
tracker_types_in_time$domain <- call_counts_in_time_nonzero$domain
tracker_types_in_time$date <- call_counts_in_time_nonzero$date
The plot below shows how the number of scripts associated with each
pattern evolved over time. There are a couple of things to note
here:
- Out of the 10 patterns suggesting tracking according to the logistic
regression model, only 5 are actually really occurring in the wild. This
is expected behavior - statistical models can be used for speculation
over fictional scenarios. I have limited further analysis to these real
patterns:
- BPC - Browser Personality and Capability
- BPRC - Browser Personality, Rendering and Capability
- C - Capability
- IBPR - Identity, Browser Personality and Rendering&Viewport
- The plot clearly shows that the predominant pattern is
Capability-only followed by the combination of Browser Personaity and
Capability.
- However, at some time between 2022 and 2023 there’s a sudden
uptick in two patterns with functions known to be involved in viewport
fingerprinting - IBPR and especially RC.

The plot below shows the same evolution of suspected tracker patterns
in both absolute and relative terms (percentages) broken down for each
major player. It is apparent that the same pattern seems to hold with
nearly all major players. Especially Google, Akamai and Adobe seem to
have sudden uptick in IBPR and RC, while Microsoft seems to always have
had a significant part of its resources aligned with the RC pattern.

Finally, I’ve decided to zoom in into the grain of individual calls.
I wanted to answer the question - how do the proportions of different
API calls differ pre and post 2022? In order to identify changes that
are significant, I’m using chi.square test over 2x2 contingency
matrices. This means that for every call I create a matrix of the
form
| X |
Calls_of_function_f |
Other_calls |
| Before 22 |
n_f_pre22 |
n_other_pre22 |
| After 22 |
n_f_post22 |
n_other_post22 |
And then apply chis.square test to derive significance p-values. I’ve
chosen the significance threshold 0.01. However, because I’m performing
a great number of these tests, the significance threshold needs to be
divided by the number of tests so that results have expected error rate
(this is called Bonferroni correction). Commentary of results is part of
the next section - Conclusion.
period_api_calls <- api_calls_in_time %>%
select(-c(region,domain,rule)) %>%
mutate(pre_2022 = date < as.Date("2022-1-1")) %>%
group_by(pre_2022,api) %>%
summarize(calls = sum(calls))
period_api_calls_wide <- period_api_calls %>%
group_by(pre_2022) %>%
mutate(period_total_calls = sum(calls)) %>%
ungroup() %>%
pivot_wider(names_from = pre_2022,values_from = c(calls,period_total_calls),values_fill = 0) %>%
mutate(period_total_calls_TRUE = ifelse(period_total_calls_TRUE == 0,period_total_calls_TRUE[period_total_calls_TRUE > 0][1],period_total_calls_TRUE))
chi_square_calls <- function(df){
calls_pre22 <- df$calls_TRUE
non_calls_pre22 <- df$period_total_calls_TRUE - df$calls_TRUE
calls_post22 <- df$calls_FALSE
non_calls_post22 <- df$period_total_calls_FALSE - df$calls_FALSE
all_data_mat <- cbind(calls_pre22,non_calls_pre22,calls_post22,non_calls_post22)
apply(all_data_mat,1,function(row){
chisq.test(rbind(
c(row["calls_pre22"],row["non_calls_pre22"]),
c(row["calls_post22"],row["non_calls_post22"])
))$p.value
})
}
period_api_calls_wide$significant <- chi_square_calls(period_api_calls_wide) < 7.874016e-05 #bonferoni correction
period_api_calls_wide %>%
filter(significant) %>%
select(api,calls_TRUE,calls_FALSE) %>%
arrange(calls_TRUE) %>%
rename(calls_before_2022 = calls_TRUE,calls_after_2022 = calls_FALSE)
Conclusion
It does seem like on top of the shift in call groups, there is a
significant difference in proportions before 2022 and after 2022 for
pretty much every individual API call. What’s more important some
heavily used API calls are completely absent before 2022. This leads me
to a couple of hypothesis explaining the sudden shift of tracking
patterns in 2022:
- There was a true change of how companies are doing fingerprinting
motivated by new approaches or change in (legislative?)
environment.
- Some API calls useful for fingerprinting were not available in JS
before 2022. Their addition motivated companies to include them in their
tracking approaches.
- The way tracker-radar collects data have changed and there’s no real
change in behavior of organizations that do
fingerprinting/tracking.
- Or any combination of above.
I have a question for you, dear reader, what do you think is the most
likely explanation? Feel free to share any ideas you have on my linkedin or via
my e-mail stanislavsmatana@gmail.com.
LS0tCnRpdGxlOiAiV2hhdCBoYXBwZW5kIGluIHdlYiB0cmFja2luZyBhcm91bmQgMjAyMj8iCmF1dGhvcjogU3RhbmlzbGF2IFNtYXRhbmEKbWFpbDogc3RhbmlzbGF2c21hdGFuYUBnbWFpbC5jb20Kb3V0cHV0OiAKICBodG1sX25vdGVib29rOgogICAgdGhlbWU6IGpvdXJuYWwKICAgIHRvYzogeWVzCi0tLQojIyMgSW50cm9kdWN0aW9uCgpIZWxsbyBkZWFyIHJlYWRlciEKCkJ5IHRoaXMgdGltZSwgeW91IHByb2JhYmx5IGtub3cgbWUgcmVhc29uYWJseSB3ZWxsIGZyb20gYWxsIHRoZSBDVnMgSSBoYXZlIHNlbnQgeW91LiBJdCBzZWVtcyBsaWtlIHRob3NlIHdlcmVuJ3QgdGhhdCBpbnRlcmVzdGluZyB0byB5b3UsIHdoaWNoIEkgdG90YWxseSB1bmRlcnN0YW5kLiBCdXQgYXQgdGhlIHNhbWUgdGltZSwgQ1YgaXMgbm90IHJlYWxseSBhYmxlIHRvIGZ1bGx5IGNvbW11bmljYXRlIGhvdyBJIGFjdHVhbGx5IHdvcmsgYW5kIHdoZXRoZXIgdGhlIHRoaW5ncyB0aGF0IEknbSBhYmxlIHRvIGNyZWF0ZSBjb3VsZCBhZGQgdmFsdWUgdG8gRHVja0R1Y2tHby4gVGhhdCdzIHdoeSBJIGhhdmUgZGVjaWRlZCB0byBzaG93IHlvdSBhIHBpZWNlIG9mIG15IHdvcmsgYnkgY29sbGVjdGluZyBhbmQgYW5hbHl6aW5nIHlvdXIgZGF0YSBmcm9tIHRoZSB0cmFja2VyLXJhZGFyIHByb2plY3QuIEkga25vdyB0aGF0IHRoaXMgbWlnaHQgYmUgYSBsaXR0bGUgYml0IHdlaXJkIGFuZCBhbm5veWluZy4gQnV0LCBJIHNlZSB3b3JraW5nIGF0IER1Y2tEdWNrR28gYXMgYSBncmVhdCBvcHBvcnR1bml0eSAtIGZhciB0b28gZ3JlYXQgdG8gZ2l2ZSB1cCByaWdodCBhd2F5LiBXaXRoIHRoYXQgYmVpbmcgc2FpZCwgaWYgeW91IGRlY2lkZSB0byByZWplY3QgbXkgYXBwbGljYXRpb24gYWdhaW4sIGFmdGVyIHNlZWluZyBhIHBpZWNlIG9mIG15IHdvcmssIEkgd2lsbCBmdWxseSBhY2NlcHQgdGhhdCBhbmQgSSB3b24ndCByZWFjaCBvdXQgYWdhaW4uIFRoYW5rIHlvdSEKCi0tIFN0YW5pc2xhdiAiU3Rhbm8iIFNtYXRhbmEKCiMjIyBXaGF0IGhhcHBlbmQgaW4gMjAyMj8gQW5hbHlzaXMgb2YgdHJhY2tlci1yYWRhciBkYXRhIGluIHRpbWUuCgoqKnRsZHI7KiogSXQgc2VlbXMgbGlrZSBzb21ldGhpbmcgaW50ZXJlc3RpbmcgaGF2ZSBoYXBwZW5kIHNvbWUgdGltZSBiZXR3ZWVuIDIwMjIgYW5kIDIwMjMgaW4gdGhlIHdvcmxkIG9mIHdlYnNpdGUgdHJhY2tlcnMuIFRoZSBuYXR1cmUgb2YgdHJhY2tlcnMgaGF2ZSBzdWRkZW5seSBzaGlmdGVkIGFuZCB0aGlzIHNoaWZ0IHNlZW1zIHRvIGJlIGNvbm5lY3RlZCB3aXRoIGZpbmdlcnByaW50aW5nIGJhc2VkIG9uIHZpZXdwb3J0IGNoYXJhY3RlcmlzdGljcy4gIERvIHlvdSBrbm93IHdoYXQgY291bGQgaGF2ZSBoYXBwZW5kPyBJZiB5b3Ugd2FudCB0byBnZXQgcmlnaHQgaW50byByZXN1bHRzLCBqdW1wIHRvIHRoZSBzZWN0aW9uIFtBbmFseXppbmcgU3VzcGVjdGVkIFRyYWNrZXIgUGF0dGVybnMgaW4gVGltZV0uIElmIHlvdSB3YW50IHRvIHNlZSBob3cgSSB3ZW50IG92ZXIgYW5hbHl6aW5nIHRoaXMgZGF0YSBzdGVwIGJ5IHN0ZXAgLSBjb250aW51ZSByZWFkaW5nLgoKSSd2ZSBkZWNpZGVkIHRvIGRvd25sb2FkIGFuZCBhbmFseXplIHdlYnNpdGUgdHJhY2tpbmcgZGF0YSBjb2xsZWN0ZWQgYnkgRHVja0R1Y2tHbydzIHRyYWNrZXItcmFkYXIgd2l0aCBmb2xsb3dpbmcgZ29hbHM6CgoqIENoYXJhY3Rlcml6ZSBwb3RlbnRpYWwgdHJhY2tlcnMgYmFzZWQgb24gY29tYmluYXRpb25zIG9mIEFQSSBjYWxscyB0aGV5IGFyZSBwZXJmb3JtaW5nLgoqIFVuZGVyc3RhbmQgaG93IGNoYXJhY3RlciBvZiB0cmFja2VycyBkaWZmZXJzIGFjcm9zcyBiaWcgcGxheWVycyBhbmQgaG93IGl0IGV2b2x2ZWQgaW4gdGltZS4KCkFsbCBvZiB0aGUgZGF0YSBJJ3ZlIHVzZWQgaW4gdGhlIGFuYWx5c2lzIHdhcyBleHRyYWN0ZWQgZnJvbSBkb21haW4ganNvbiBmaWxlcyBhdmFpbGlhYmxlIGFjcm9zcyBhbGwgcmVsZWFzZXMgb2YgW10oaHR0cHM6Ly9naXRodWIuY29tL2R1Y2tkdWNrZ28vdHJhY2tlci1yYWRhcikuIFlvdSBjYW4gc2VlIHRoZSBzaGFwZSBvZiB0aGUgcmF3IGlucHV0IGRhdGEgYmVsb3cuIEVhY2ggcmVjb3JkcyBjb3JyZXNwb25kcyB0byBvbmUgdHlwZSBvZiBBUEkgY2FsbCBpbiBvbmUgcmVzb3VyY2Ugb2Ygb25lIGRvbWFpbiwgbWVhbmluZyB0aGF0IHRoZSBvdmVyYWxsIGdyYWluIG9mIHRoZSB0YWJsZSBpcyByZWxlYXNlX3RhZyAoZGF0ZSksIHJlZ2lvbiwgZG9tYWluLCByZXNvdXJjZSwgYXBpIGNhbGwuIFRoZSBkYXRhIGNvbnRhaW5zIGZvbGxvd2luZyBjb2x1bW5zOgoKKiAqKmRhdGUqKiAtIGRhdGUgb2YgZGF0YSBjb2xsZWN0aW9uLCBleHRyYWN0ZWQgZnJvbSByZWxlYXNlX3RhZwoqICoqcmVnaW9uKiogLSByZWdpb24gaW4gd2hpY2ggdGhpcyByZWNvcmQgd2FzIGNvbGxlY3RlZCAoY29ycmVzcG9uZHMgdG8gc3ViZm9sZGVycyBvZiBkb21haW4gZm9sZGVyKQoqICoqcnVsZSoqIC0gcmVnZXggaWRlbnRpZnlpbmcgdGhlIHJlc291cmNlCiogKiphcGkqKiAtIGFwaSBmdW5jdGlvbiAgCiogKipjYWxscyoqIC0gbnVtYmVyIG9mIGNhbGxzIHRvIHRoZSBhcGkgZnVuY3Rpb24KKiAqKmZpbmdlcnByaW50aW5nKiogLSBmaW5nZXJwcmludGluZyBzY29yZSBkZXJpdmVkIGluIHRyYWNrZXItcmFkYXIgZm9yIHRoZSByZXNvdXJjZQoKCmBgYHtyfQpsaWJyYXJ5KGRwbHlyKQpsaWJyYXJ5KHRvcGljbW9kZWxzKQpsaWJyYXJ5KHRpZHlyKQoKYXBpX2NhbGxzX2luX3RpbWUgPC0gcmVhZC5jc3YoImRhdGEvYXBpX2NhbGxzX2J5X3JlbGVhc2UuY3N2IikgJT4lCiAgZmlsdGVyKHR5cGUgPT0gIlNjcmlwdCIpIAoKI2NvbnZlcnRpbmcgcmVsZWFzZSB0YWdzIHRvIGRhdGVzCnJlbGVhc2VfdGFnX3RvX2RhdGUgPC0gZGF0YS5mcmFtZShyZWxlYXNlX3RhZyA9IHVuaXF1ZShhcGlfY2FsbHNfaW5fdGltZSRyZWxlYXNlX3RhZykpICU+JQogIHNlcGFyYXRlKHJlbGVhc2VfdGFnLCBpbnRvID0gYygieWVhciIsICJtb250aCIsICJkYXkiKSwgc2VwID0gIlxcLiIsIGZpbGwgPSAicmlnaHQiLHJlbW92ZSA9IEYpICU+JQogIG11dGF0ZSgKICAgIG1vbnRoID0gc3ByaW50ZigiJTAyZCIsIGFzLmludGVnZXIobW9udGgpKSwKICAgIGRheSAgID0gaWZlbHNlKGlzLm5hKGRheSksICIwMSIsIHNwcmludGYoIiUwMmQiLCBhcy5pbnRlZ2VyKGRheSkpKSwKICAgIGRhdGUgID0gYXMuRGF0ZShwYXN0ZSh5ZWFyLCBtb250aCwgZGF5LCBzZXAgPSAiLSIpKQogICkgJT4lCiAgc2VsZWN0KHJlbGVhc2VfdGFnLGRhdGUpCgphcGlfY2FsbHNfaW5fdGltZSA8LSBhcGlfY2FsbHNfaW5fdGltZSAlPiUKICBpbm5lcl9qb2luKHJlbGVhc2VfdGFnX3RvX2RhdGUpICU+JQogIHNlbGVjdChkYXRlLHJlZ2lvbixkb21haW4scnVsZSxhcGksY2FsbHMsZmluZ2VycHJpbnRpbmcpCiAgCmhlYWQoYXBpX2NhbGxzX2luX3RpbWUpCmBgYAoKUHl0aG9uIHNjcmlwdHMgSSd2ZSB1c2VkIHRvIHNjcmFwZSB0cmFja2VyLXJhZGFyIHRvZ2V0aGVyIHdpdGggdGhlIHNvdXJjZSBjb2RlIG9mIHRoaXMgbm90ZWJvb2sgYW5kIG90aGVyIGFydGlmYWN0cyBhcmUgYXZhaWxpYWJsZSBoZXJlIFRPRE8uCgpOT1RFOiBQeXRob24gaXMgbXkgcHJpbWFyeSBsYW5ndWFnZSwgYnV0IEkgbG92ZSB0byB1c2UgUiBmb3IgZXhwbG9yYXRvcnkgZGF0YSBhbmFseXNpcywgc28gdGhhdCdzIHdoYXQgSSdtIHVzaW5nIGhlcmUgOikgCgojIyMjIEFJIFVzZSBEdXJpbmcgdGhpcyBBbmFseXNpcwpEdXJpbmcgdGhpcyBwcm9jZXNzIEkndmUgdXNlZCBBSSAoR2l0SHViIENvcGlsb3QpIGF0IHRpbWVzIHRvIGNvbXBsZW1lbnQgbXkgcHJpbWFyeSBza2lsbHMuIEkgaGF2ZSB1c2VkIEFJIGV4YWN0bHkgaW4gdGhpcyB3YXk6CgoqIEkgKipoYXZlIHVzZWQgQUkqKiB0byBnZW5lcmF0ZSBweXRob24gc2NyaXB0cyBmb3Igc2NyYXBpbmcgb2YgW10oaHR0cHM6Ly9naXRodWIuY29tL2R1Y2tkdWNrZ28vdHJhY2tlci1yYWRhciksIGJlY2F1c2UgSSBkaWRuJ3Qgd2FudCB0byB3YXN0ZSB0aW1lIGltcGxlbWVudGluZyBzY3JhcGluZyBhbmQgdG8gZm9jdXMgb24gYW5hbHlzaXMuCiogSSBkaWQgKipub3QgdXNlKiogQUkgdG8gY29uZHVjdCB0aGUgYW5hbHlzaXMgb2YgdGhpcyBkYXRhIGFuZCBjcmVhdGUgdGhpcyBub3RlYm9vaywgYmVjYXVzZSB0aGF0J3MgdGhlIHBhcnQgdGhhdCBJIGVuam95LCB3aGVyZSBJIGV4Y2VsLCBhbmQgd2hlcmUgSSBkbyBhIGJldHRlciBqb2IuCiogSSAqKmhhdmUgdXNlZCBBSSoqIHRvIGhlbHAgaW50ZXJwcmV0IExEQSB0b3BpY3MgKGRldGFpbHMgaW4gdGhlIG5leHQgc2VjdGlvbiksIGJlY2F1c2UgbXkga25vd2xlZGdlIG9mIHdlYiB0cmFja2VycyBpcyB2ZXJ5IGxpbWl0ZWQgOikgCgoKIyMjIENoYXJhY3Rlcml6aW5nIEFQSSBDYWxscyAtIExhdGVudCBEaXJpY2hsZXQgQWxsb2NhdGlvbgoKRmluZ2VycHJpbnRpbmcgaXMgY29ubmVjdGVkIHRvIGNlcnRhaW4gSmF2YXNjcmlwdCBBUEkgY2FsbHMgdGhhdCBjYW4gaGVscCBjcmVhdGUgcHJlY2lzZSBpZGVudGlmaWNhdGlvbiBvZiBhIGRldmljZSBvciBhIHVzZXIuIE15IGFzc3VtcHRpb24gaXMgdGhhdCB0aGVyZSBhcmUgY2VydGFpbiB0eXBlcyBvZiB0cmFja2VycyBhbmQgdGhhdCBlYWNoIHRyYWNrZXIgdHlwZSBpcyBhc3NvY2lhdGVkIHdpdGggY2VydGFpbiBBUEkgY2FsbHMuIEluIG9yZGVyIHRvIHNlZSB3aGF0IHRoZXNlIHN1c3BlY3RlZCB0cmFja2VyIHR5cGVzIG1pZ2h0IGJlLCBJIG5lZWQgZmlyc3QgdG8gaWRlbnRpZnkgQVBJIGNhbGxzIHRoYXQgdGVuZCB0byBvY2N1ciB0b2dldGhlciAtIEFQSSBjYWxsIGdyb3Vwcy4KCk5lYXJseSBhbnkgY2x1c3RlcmluZyBhcHByb2FjaCBjb3VsZCBiZSB1c2VkIHRvIGNyZWF0ZSBzb21lIGdyb3VwcyBvZiBBUEkgY2FsbHMsIGJ1dCBJIGhhdmUgZGVjaWRlZCB0byB1c2UgKkxhdGVudCBEaXJpY2hsZXQgQWxsb2NhdGlvbiogKExEQSwgbm90IHRvIGJlIGNvbmZ1c2VkIHdpdGggTGluZWFyIERpc2NyaW1pbmFudCBBbmFseXNpcykuIFdoaWxlIHRoaXMgbW9kZWwgdHJhZGl0aW9uYWxseSBjb21lcyBmcm9tIHRoZSBmaWVsZCBvZiBOYXR1cmFsIExhbmd1YWdlIFByb2Nlc3NpbmcsIGl0cyBhc3N1bXB0aW9ucyBleGFjdGx5IG1hdGNoIHRoaXMgc2V0dGluZyAtIHNwYXJzZSBjb3VudCBkYXRhIChjb3VudHMgb2YgYXBpIGNhbGxzIHdpdGggb25seSBmZXcgYXBwZWFyaW5nIGluIGVhY2ggcmVzb3VyY2UpLiBEb24ndCBiZSBzY2FyZWQgYnkgdGhlIGNvbXBsaWNhdGVkIG5hbWUsIGl0J3MgYWN0dWFsbHkgYSBmYWlybHkgc2ltcGxlIHVuc3VwZXJ2aXNlZCBnZW5lcmF0aXZlIG1vZGVsLiBMZXQgbWUgZXhwbGFpbiBob3cgaXQgc3BlY2lmaWNhbGx5IHdvcmtzIGZvciB0aGlzIGFuYWx5c2lzLgoKRXZlcnkgc3RhdGlzdGljYWwgbW9kZWwgYXNzdW1lcyBjZXJ0YWluIHZpZXcgb2YgdGhlIHdvcmxkIGFuZCB0aGVuIGhlbHBzIHVzIGRlcml2ZSBuZXcgaW5mb3JtYXRpb24gYmFzZWQgb24gdGhpcyB2aWV3LiBJbiB0aGlzIHNldHRpbmcsIExEQSBhc3N1bWVzIHRoYXQgY2VydGFpbiBBUEkgY2FsbHMgdGVuZCB0byBvY2N1ciB0b2dldGhlci4gVGhlc2UgY28tb2N1cmluZyBjYWxscyBjYW4gYmUgZ3JvdXBlZCBpbnRvIGxhcmdlciBjYWxsIGdyb3Vwcy4gRWFjaCBjYWxsIGNhbiBiZSBhIG1lbWJlciBvZiBtdWx0aXBsZSBncm91cHMgYW5kIHRoaXMgbWVtYmVyc2hpcCBpcyBzb2Z0LiBUaGlzIG1lYW5zIHRoYXQgZWFjaCBjYWxsIGlzIGFzc29jaWF0ZWQgd2l0aCBlYWNoIGdyb3VwICh0b3BpYyBpbiBMREEgbGFuZ3VhZ2UpIHdpdGggY2VydGFpbiBwcm9iYWJpbGl0eSBiZXR3ZWVuIDAgYW5kIDEuIAoKV2l0aCBncm91cHMgZXN0YWJsaXNoZWQsIHNjcmlwdCByZXNvdXJjZXMgKGRvY3VtZW50cyBpbiBMREEgbGFuZ3VhZ2UpIGFyZSB2aWV3ZWQgYXMgZGlmZmVyZW50IG1peGVzIG9mIGdyb3Vwcy4gRWFjaCBtaXggaXMgY2hhcmFjdGVyaXplZCBieSByZXNvdXJjZSB0byBncm91cCBhc3NvY2lhdGlvbnMgYmV0d2VlbiAwLTEgdGhhdCBhZGQgdXAgdG8gMS4gR3JvdXBzLCBhc3NvY2lhdGlvbnMgYmV0d2VlbiByZXNvdXJjZXMgYW5kIGdyb3VwcyBhbmQgYXNzb2NpYXRpb25zIGJldHdlZW4gZ3JvdXBzIGFuZCBjYWxscyBhcmUgZGVyaXZlZCBieSBMREEgZnJvbSBkYXRhIHdpdGhvdXQgc3VwZXJ2aXNpb24uIFRoZSBvbmx5IHJlcXVpcmVkIHBhcmFtZXRlciBpcyB0aGUgbnVtYmVyIG9mIGdyb3VwcyAodG9waWNzKS4gSSB3aWxsIGRlc2NyaWJlIHNlbGVjdGlvbiBvZiB0aGUgbnVtYmVyIG9mIGdyb3VwcyBsYXRlci4gCgpUaGUgZGlhZ3JhbSBiZWxvdyBpbGx1c3RyYXRlcyBob3cgTERBIG1vZGVscyBhbiBleGFtcGxlIG9mIDIgc2NyaXB0IHJlc291cmNlcy4gRWFjaCBzY3JpcHQgaXMgYXNzb2NpYXRlZCB3aXRoIGNhbGwgZ3JvdXBzIDEtNCBhbmQgaXRzIGdyb3VwIG1peCBkaWN0YXRlcyB0aGUgYWN0dWFsIGNhbGxzIHRoYXQgYXBwZWFyIGluIGVhY2ggc2NyaXB0IHJlc291cmNlLiBGb3IgaW5zdGFuY2UsIGV4YW1wbGUxLmpzIGlzIDcwJSBncm91cDEgYW5kIDcwJSBncm91cDIuIFRoZXJlZm9yZSwgaXRzIEFQSSBjYWxscyBjb21lIG1vc3RseSBmcm9tIGdyb3VwMiBhbmQgdGhlbiBhbHNvIGZyb20gZ3JvdXAxLiBUaGlzIGlzIGEgdG95IGV4YW1wbGUsIGluIHJlYWwgTERBLCBlYWNoIGRvY3VtZW50IGlzIHVzdWFsbHkgYXNzb2NpYXRlZCB3aXRoIGVhY2ggdG9waWMgdG8gYW4gZXh0ZW50LgoKV2hhdCBkb2VzIExEQSBlbmFibGUgbWUgdG8gZG8/CgoqIFJlZHVjZSBkaW1lbnNpb25hbGl0eSBpbiBhbiBpbnRlcnByZXRhYmxlIHdheS4gSW5zdGVhZCBvZiBjaGFyYWN0ZXJpemluZyByZXNvdXJjZXMgYnkgYWxsIGFwaSBjYWxsIHR5cGVzLCBJIGNhbiBjaGFyYWN0ZXJpemUgdGhlbSBieSB3aG9sZSBjYWxsIGdyb3Vwcy4KKiBVbmRlcnN0YW5kIHdoaWNoIEFQSSBjYWxscyB0ZW5kIHRvIGNvLW9jY3VyIGFuZCB1bmRlcnN0YW5kIHdoYXQgd2hhdCB0aGVzZSBncm91cHMgb2YgY28tb2NjdXJpbmcgY2FsbHMgbWlnaHQgcmVwcmVzZW50LgoKCiFbXShmaWcvbGRhX2Zvcl9hcGlfY2FsbHMuanBlZykKCkluIG9yZGVyIHRvIGRlcml2ZSBjYWxsIGdyb3VwcyBJIHdpbGwgdXNlIG9ubHkgZGF0YSBmcm9tIHRoZSBtb3N0IHJlY2VudCByZWxlYXNlLiBBZnRlcndhcmRzLCBJJ2xsIGFwcGx5IHRoZSBtb2RlbCB0byB0aGUgZnVsbCBkYXRhIHNwYW5uaW5nIGFsbCB0cmFja2VyLXJhZGFyIHJlbGVhc2VzLiBJIGRvIHRoaXMgYmVjYXVzZSBJIGFzc3VtZSB0aGF0IHRvZGF5IHdlIGhhdmUgdGhlIGJyb2FkZXN0IHNwZWN0cnVtIG9mIHRyYWNraW5nIHRlY2huaXF1ZXMgYXZhaWxhYmxlIGFuZCB0aGUgcGFzdCB3YXMgZXF1YWxseSBvciBsZXNzIGRpdmVyc2UuCgoKYGBge3J9CmFwaV9jYWxsc19yZWNlbnQgPC0gCiAgYXBpX2NhbGxzX2luX3RpbWUgJT4lCiAgZmlsdGVyKGRhdGUgPT0gbWF4KGFwaV9jYWxsc19pbl90aW1lJGRhdGUpKQoKaGVhZChhcGlfY2FsbHNfcmVjZW50KQpgYGAKCk9uZSBsYXN0IHRoaW5nIEkgbmVlZCB0byBkbyBpbiBvcmRlciB0byBhcHBseSBMREEgaXMgdG8gZGV0ZXJtaW5lIHRoZSBudW1iZXIgb2YgY2FsbCBncm91cHMgKHRvcGljcykgLSBrLiBUaGVyZSBhcmUgbWFueSBtZXRob2RzIG9mIGRvaW5nIHRoYXQuIEkgaGF2ZSBkZWNpZGVkIHRvIHVzZSBvbmUgYmFzZWQgb24gY29pc25lIHNpbWlsYXJpdHkgKENhbyBldC4gYWwuIDIwMDkpLCB3aGljaCBhaW1zIHRvIGNyZWF0ZSBncm91cHMgdGhhdCBhcmUgYXMgZGlzdGluY3QgZnJvbSBvbmUgYW5vdGhlciBhcyBwb3NzaWJsZS4gVGhpcyBpcyBoZWxwZnVsIGJlY2F1c2UgTERBIHNvbWV0aW1lcyB0ZW5kcyB0byBjcmVhdGUgZ3JvdXBzICh0b3BpY3MpIHRoYXQgYXJlIGhlYXZpbHkgb3ZlcmxhcHBpbmcuIFByZXNlbmNlIG9mIG11bHRpcGxlIG92ZXJsYXBwaW5nIGdyb3VwcyB0aGF0IGNvcnJlc3BvbmQgcm91Z2hseSB0byB0aGUgc2FtZSBjb25jZXB0IGNyZWF0ZXMgbm9pc2UgdGhhdCBtYWtlcyBpbnRlcnByZXRhdGlvbiBoYXJkZXIuCgpNb3JlIGZvcm1hbGx5LCBrLCBmb3Igd2hpY2ggdGhlIG1vZGVsIGhhcyBncm91cHMgd2l0aCBzbWFsbGVzdCBhdmVyYWdlIHBhaXJ3aXNlIGNvc2luZSBzaW1pbGFyaXR5IGlzIHNlbGVjdGVkIGFzIHRoZSB3aW5uaW5nIGsuCgpgYGB7cn0KY29zaW5lX3NpbSA8LSBmdW5jdGlvbihNKSB7CiAgIyByb3cgbm9ybXMKICBub3JtcyA8LSBzcXJ0KHJvd1N1bXMoTSAqIE0pKQogIAogICMgZGVub21pbmF0b3IgdmlhIG91dGVyCiAgZGVuIDwtIG91dGVyKG5vcm1zLCBub3JtcywgIioiKQogIAogICMgZG90IHByb2R1Y3RzCiAgZG90IDwtIE0gJSolIHQoTSkKICAKICAjIGNvc2luZSBzaW1pbGFyaXR5CiAgY29zX3NpbSA8LSBkb3QgLyBkZW4KICBjb3Nfc2ltCn0KCmF2Z19jb3Nfc2ltIDwtIGZ1bmN0aW9uKE0pewogIGNzaW0gPC0gY29zaW5lX3NpbShNKQogIG1lYW4oY3NpbVt1cHBlci50cmkoY3NpbSldKSAgCn0KCmNhbGxfY291bnRzIDwtIGFwaV9jYWxsc19yZWNlbnQgJT4lCiAgcGl2b3Rfd2lkZXIobmFtZXNfZnJvbT1hcGksdmFsdWVzX2Zyb20gPSBjYWxscyx2YWx1ZXNfZmlsbCA9IDApIAoKY2FsbF9jb3VudHNfbWF0IDwtIGNhbGxfY291bnRzICU+JSAKICBzZWxlY3QoLWMocmVnaW9uLGRvbWFpbixydWxlLGZpbmdlcnByaW50aW5nLGRhdGUpKSAlPiUgCiAgYXMubWF0cml4KCkKCiNUaGlzIHRha2VzIGEgbG90IG9mIHRpbWUsIGp1c3QgbG9hZCB0aGUgcmRzIGlmIHdpbm5lciB3YXMgYWxyZWFkeSBjYWxjdWxhdGVkCmlmKGZpbGUuZXhpc3RzKCJkYXRhL3dpbm5pbmdfbGRhLlJkcyIpKXsKICB3aW5uaW5nX2xkYSA9IHJlYWRSRFMoImRhdGEvd2lubmluZ19sZGEuUmRzIikKfWVsc2UKewogIGNzaW1zIDwtIHJlcCgwLHRpbWVzPTE5KQogIGZvcihrIGluIDI6MjApewogICAgY2F0KHBhc3RlKCJQcm9jZXNzaW5nIExEQSAiLGssIlxuIikpCiAgICB0b3BpY19tb2RlbCA8LSBMREEoY2FsbF9jb3VudHNfbWF0LGspICAgIAogICAgY3NpbXNbay0xXSA8LSBhdmdfY29zX3NpbSh0b3BpY19tb2RlbEBiZXRhKQogIH0KCiAgCiAgc2VsZWN0ZWRfayA8LSAoMjoyMClbd2hpY2gubWluKGNzaW1zKV0KICB3aW5uaW5nX2xkYSA9IExEQShjYWxsX2NvdW50c19tYXQsc2VsZWN0ZWRfaykKICAKICAKfQoKc2VsZWN0ZWRfayA9IG5jb2wod2lubmluZ19sZGFAZ2FtbWEpCgpgYGAKCkxEQSB3aXRoIDQgdG9waWNzIHdhcyBzZWxlY3RlZCBhcyB0aGUgbW9kZWwgd2l0aCB0aGUgbG93ZXN0IGF2ZXJhZ2UgY29zaW5lIHNpbWlsYXJpdHkgYmV0d2VlbiBBUEkgY2FsbCBncm91cHMgKHRvcGljcykuIE5leHQsIEkgd2FudCB0byBjaGFyYWN0ZXJpemUgZWFjaCBncm91cCAodG9waWMpIGFuZCBmb3IgdGhhdCBJJ2xsIGlkZW50aWZ5IGNhbGxzIHRoYXQgYXJlIG1vc3QgY2hhcmFjdGVyaXN0aWMgZm9yIGVhY2ggZ3JvdXAuIFRoaXMgY2FuIGJlIGRvbmUgYnkgbG9va2luZyBpbnRvIHRoZSBiZXRhIG1hdHJpeCB0aGF0IGhhcyA0IHJvd3MsIG9uZSBmb3IgZWFjaCBncm91cCwgIGFuZCBhcyBtYW55IGNvbHVtbnMgYXMgdGhlcmUgYXJlIGRpc3RpbmN0IGNhbGwgdHlwZXMuIEVhY2ggY2VsbCBjb3JyZXNwb25kcyB0byBwcm9iYWJpbGl0eSAoaW4gbG9nIHNjYWxlKSBvZiBjb3JyZXNwb25kaW5nIGNhbGwgYmVpbmcgYXNzb2NpYXRlZCB3aXRoIGEgY29ycmVzcG9uZGluZyBjYWxsIGdyb3VwLiBTbWFsbCBleGNlcnB0IG9mIHRoZSBtYXRyaXggYmVsb3c6CgpgYGB7cn0gCmFzc29jX21hdCA8LSB3aW5uaW5nX2xkYUBiZXRhWywxOjNdCmNvbG5hbWVzKGFzc29jX21hdCkgPC0gd2lubmluZ19sZGFAdGVybXNbMTozXQpyb3duYW1lcyhhc3NvY19tYXQpIDwtIHBhc3RlKCJHcm91cCIsMTo0KQphc3NvY19tYXQKYGBgCkhvd2V2ZXIsIGl0IGlzIGltcG9ydGFudCB0byBub3RlIHRoYXQgaWYgYSBjYWxsIGhhcyBoaWdoIHByb2JhYmlsaXR5IG9mIGFwcGVhcmluZyBpbiBhIGdpdmVuIGdyb3VwLCBpdCBkb2Vzbid0IGF1dG9tYXRpY2FsbHkgbWVhbiB0aGF0IGl0IGlzIHZlcnkgY2hhcmFjdGVyaXN0aWMgb2YgdGhpcyBncm91cC4gSXQgbWlnaHQgYmUgdGhlIGNhc2UgdGhhdCBpdCBpcyBhIHZlcnkgY29tbW9uIGNhbGwgdGhhdCBoYXMgZXF1YWxseSBoaWdoIHByb2JhYmlsaXR5IG9mIGFwcGVhcmluZyBpbiBlYWNoIGdyb3VwLiAKCkx1Y2tpbHksIGl0IGlzIHBvc3NpYmxlIHRvIHVzZSBCYXllcyBydWxlIHRvIGNhbGN1bGF0ZSB0aGUgcHJvYmFiaWxpdHkgb2YgY2FsbCBiZWluZyAqKmNoYXJhY3RlcmlzdGljKiogb2YgYSBnaXZlbiBncm91cC4gVGhpcyBpcyBkb25lIHNpbXBseSBieSBub3JtYWxpemluZyB0aGUgY29sdW1ucyBvZiB0aGUgYmV0YSBtYXRyaXggaW50byAwLTEgcmFuZ2UuIEkgaGF2ZSB1c2VkIGxvZ3N1bWV4cCBhbmQgc3VidHJhY3Rpb24gYmVmb3JlIGFwcGx5aW5nIGV4cC4gVGhlIHNhbWUgcmVzdWx0IGNvdWxkIGJlIGFjaGlldmVkIGJ5IGV4cG9uZW50aWF0aW5nIGZpcnN0IGFuZCB0aGVuIG5vcm1hbGl6aW5nIHVzaW5nIGRpdmlzaW9uIGJ5IHN1bSBvZiBwcm9iYWJpbGl0aWVzLiBIb3dldmVyLCBjYWxjdWxhdGlvbnMgaW4gbG9nIHNjYWxlIGFyZSBudW1lcmljYWxseSBtb3JlIHN0YWJsZSwgc28gaXQgbWFrZXMgc2Vuc2UgdG8gc3RheSBpbiB0aGUgbG9nIHNjYWxlIGFzIGxvbmcgYXMgcG9zc2libGUuCgpOZXh0LCBJIGlkZW50aWZ5IHRoZSBtb3N0IGNoYXJhY3RlcmlzdGljIEFQSSBjYWxscyBmb3IgZWFjaCBncm91cCAtIEFQSSBjYWxscyB0aGF0IGhhdmUgcHJvYmFiaWxpdHkgb2YgYmVpbmcgY2hhcmFjdGVyaXN0aWMgb2YgYSBnaXZlbiBncm91cCBoaWdoZXIgdGhhbiA4MCUuCgpgYGB7cn0KbGlicmFyeShtYXRyaXhTdGF0cykKY2hhcmFjdGVyaXN0aWNfdGVybXMgPC0gZnVuY3Rpb24obGRhX21vZGVsKXsKICAKICBzdW1zIDwtIGFwcGx5KGxkYV9tb2RlbEBiZXRhLDIsbG9nU3VtRXhwKQogIHBfZGlzdGluY3RpdmUgPC0gZXhwKHN3ZWVwKGxkYV9tb2RlbEBiZXRhLDIsc3VtcykpICAKICAKICBuX3RvcGljcyA8LSBucm93KGxkYV9tb2RlbEBiZXRhKQogIHJlc3VsdHMgPC0gbGlzdCgpCiAgCiAgZm9yKGkgaW4gMTpuX3RvcGljcyl7CiAgICBvcmRlcmluZyA8LSBvcmRlcihsZGFfbW9kZWxAYmV0YVtpLF0sZGVjcmVhc2luZyA9IFQpCiAgICBvcmRlcmVkX3Rlcm1zIDwtIGxkYV9tb2RlbEB0ZXJtc1tvcmRlcmluZ10KICAgIG9yZGVyZWRfc2VsZWN0aW9uIDwtIChwX2Rpc3RpbmN0aXZlW2ksXSA+IDAuOClbb3JkZXJpbmddCiAgICBzZWxlY3RlZF9vcmRlcmVkX3Rlcm1zIDwtIG9yZGVyZWRfdGVybXNbb3JkZXJlZF9zZWxlY3Rpb25dCiAgICBvcmRlcmVkX3BzIDwtIChleHAobGRhX21vZGVsQGJldGFbaSxdKVtvcmRlcmluZ10pW29yZGVyZWRfc2VsZWN0aW9uXSAgICAgIAogICAgbmFtZXMob3JkZXJlZF9wcykgPC0gc2VsZWN0ZWRfb3JkZXJlZF90ZXJtcwogICAgCiAgICByZXN1bHRzW1tpXV0gPC0gb3JkZXJlZF9wcwogIH0KICAKICByZXN1bHRzCgp9CgpjaGFyYWN0ZXJpc3RpY19jYWxscyA8LSBjaGFyYWN0ZXJpc3RpY190ZXJtcyh3aW5uaW5nX2xkYSkKCmBgYAoKQmVjYXVzZSBteSBrbm93bGVkZ2Ugb2Ygd2ViIHRyYWNraW5nIG1ldGhvZHMgaXMgZmFpcmx5IGxpbWl0ZWQsIEkgaGF2ZSBjb25zdWx0ZWQgQUkgYXQgdGhpcyBzdGFnZS4gRm9yIGVhY2ggZ3JvdXAsIEkgaGF2ZSB0YWtlbiB0aGUgY2hhcmFjdGVyaXN0aWMgQVBJIGNhbGxzIGFuZCBhc2tlZCBBSSAoQ29waWxvdCkgdGhlc2UgcXVlc3Rpb25zOgoKKiAiV2hhdCB0aGVzZSBqYXZhc2NyaXB0IGZ1bmN0aW9uIGNhbGxzIGhhdmUgaW4gY29tbW9uPyIKKiAiSG93IHdvdWxkIHlvdSBjaGFyYWN0ZXJpemUgdGhpcyBncm91cD8iIAoqICJXaGF0IG5hbWUgd291bGQgeW91IGdpdmUgdG8gdGhpcyBncm91cCBvZiBmdW5jdGlvbnM/IgoKSSBoYXZlIG1hZGUgc3VyZSB0byBhc2sgdGhlc2UgcXVlc3Rpb25zIHdpdGhvdXQgbWVudGlvbmluZyB3ZWIgdHJhY2tpbmcuIEludGVyZXN0aW5nbHksIGZvciBlYWNoIGdyb3VwLCB0aGUgQUkgaGF2ZSBpbW1lZGlhdGVseSBpZGVudGlmaWVkIHRoYXQgdHJhY2tpbmcvZmluZ2VycHJpbnRpbmcgbWlnaHQgYmUgYXQgcGxheS4gQW5zd2VycyBoZWxwZWQgbWUgZXN0YWJsaXNoIG5hbWVzIGFuZCBjaGFyYWN0ZXJpemF0aW9uIG9mIHRoZXNlIEFQSSBjYWxsIGdyb3Vwcy4KClRoZSBzZXJpZXMgb2YgYmFycGxvdHMgYmVsb3cgc2hvd3MgY2hhcmFjdGVyaXN0aWMgY2FsbHMgZm9yIGVhY2ggY2FsbCBncm91cC4gVGhlIGxlbmd0aCBvZiB0aGUgYmFyIGNvcnJlc3BvbmRzIHRvIGhvdyBmcmVxdWVudGx5IGlzIGdpdmVuIGNhbGwgYXNzb2NpYXRlZCB3aXRoIGEgZ2l2ZW4gZ3JvdXAuIAoKIyMjIyBJZGVudGl0eSAmIFBlcnNpc3RhbmNlIEFQSSBDYWxsIEdyb3VwCkZ1bmN0aW9ucyBmb2N1c2VkIG9uIHBlcnNpc3RhbnRlIHN0b3JhZ2UgYW5kIGhpZ2gtZW50cm9weSBpZGVudGlmaWVycy4KYGBge3IsIGVjaG89Rn0Kb2xkX3BhciA8LSBwYXIoKQpwYXIobWFyID0gYyg1LjEsIDEzLCA0LjEsIDIuMSkpCmJhcnBsb3QoY2hhcmFjdGVyaXN0aWNfY2FsbHNbWzFdXSxuYW1lcy5hcmcgPSBuYW1lcyhjaGFyYWN0ZXJpc3RpY19jYWxsc1tbMV1dKSxsYXM9MixjZXgubmFtZXM9MC42LGhvcml6ID0gVCx4bGFiPSJQcm9iYWJpbGl0eSBvZiBBc3NvY2lhdGlvbiIpCnBhcihvbGRfcGFyKQoKYGBgCiMjIyMgQnJvd3NlciBQZXJzb25hbGl0eSBBUEkgQ2FsbCBHcm91cApDYWxscyBmb2N1c2VkIG9uIGNoYXJhY3Rlcml6aW5nIHVzZXIncyBicm93c2VyLiBUaGlzIGlzIGEgYnJvYWQgZ3JvdXAgLSBsaW1pdGluZyB0byB0b3AgMTUgbW9zdCBjb21tb24gY2FsbHMuCgpgYGB7ciwgZWNobz1GfQpsaW1pdGVkX3NldCA8LSBjaGFyYWN0ZXJpc3RpY19jYWxsc1tbMl1dWzE6MTVdCm9sZF9wYXIgPC0gcGFyKCkKcGFyKG1hciA9IGMoNS4xLCAxMywgNC4xLCAyLjEpKQpiYXJwbG90KGxpbWl0ZWRfc2V0LG5hbWVzLmFyZyA9IG5hbWVzKGxpbWl0ZWRfc2V0KSxsYXM9MixjZXgubmFtZXM9MC42LGhvcml6ID0gVCx4bGFiPSJQcm9iYWJpbGl0eSBvZiBBc3NvY2lhdGlvbiIpCnBhcihvbGRfcGFyKQpgYGAKIyMjIyBSZW5kZXJpbmcgJiBWaWV3aW5nIFN1cmZhY2UgQVBJIENhbGwgR3JvdXAKTW9zdGx5IHByb3BlcnRpZXMgb2YgdGhlIHZpZXdwb3J0LCByZW5kZXJpbmcgcXVpcmtzIGFuZCBtb3Rpb24gc2Vuc29yLgoKYGBge3IsIGVjaG89Rn0Kb2xkX3BhciA8LSBwYXIoKQpwYXIobWFyID0gYyg1LjEsIDEzLCA0LjEsIDIuMSkpCmJhcnBsb3QoY2hhcmFjdGVyaXN0aWNfY2FsbHNbWzNdXSxuYW1lcy5hcmcgPSBuYW1lcyhjaGFyYWN0ZXJpc3RpY19jYWxsc1tbM11dKSxsYXM9MixjZXgubmFtZXM9MC42LGhvcml6ID0gVCx4bGFiPSJQcm9iYWJpbGl0eSBvZiBBc3NvY2lhdGlvbiIpCnBhcihvbGRfcGFyKQpgYGAKIyMjIyBDYXBhYmlsaXR5ICYgSGFyZHdhcmUgQVBJIENhbGwgR3JvdXAKTW9zdGx5IGZ1bmN0aW9ucyB0aGF0IGNhbiBiZSB1c2VkIHRvIGdldCBicm9hZCBpbmZvcm1hdGlvbiBvbiBoYXJkd2FyZSBhbmQgZGV2aWNlIGNhcGFiaWxpdGllcy4gQmlnIGdyb3VwLCBzaG93aW5nIG9ubHkgdG9wIDE1LgoKYGBge3IsIGVjaG89Rn0KbGltaXRlZF9zZXQgPC0gY2hhcmFjdGVyaXN0aWNfY2FsbHNbWzRdXVsxOjE1XQpvbGRfcGFyIDwtIHBhcigpCnBhcihtYXIgPSBjKDUuMSwgMTMsIDQuMSwgMi4xKSkKYmFycGxvdChsaW1pdGVkX3NldCxuYW1lcy5hcmcgPSBuYW1lcyhsaW1pdGVkX3NldCksbGFzPTIsY2V4Lm5hbWVzPTAuNixob3JpeiA9IFQseGxhYj0iUHJvYmFiaWxpdHkgb2YgQXNzb2NpYXRpb24iKQpwYXIob2xkX3BhcikKYGBgCgojIyMgQVBJIENhbGwgR3JvdXBzIGFuZCBTdXNwZWN0ZWQgVHJhY2tpbmcKCkFwcGxpY2F0aW9uIG9mIExEQSBhYm92ZSBoYXZlIGhlbHBlZCB0byBpZGVudGlmeSBjZXJ0YWluIGdyb3VwcyBvZiBjYWxscyB0aGF0IHRlbmQgdG8gY28tb2NjdXIuIFRoaXMgZG9lcyBub3QgbWVhbiwgaG93ZXZlciwgdGhhdCBhbGwgb2YgdGhlbSBhcmUgaW5kaWNhdGlvbiBvZiBmaW5nZXJwcmludGluZy4gSW4gb3JkZXIgdG8gc2VlIHdoaWNoIGdyb3VwcyBvciBncm91cCBjb21iaW5hdGlvbnMgbWlnaHQgYmUgYXNzb2NpYXRlZCB3aXRoIGZpbmdlcnByaW50aW5nLCBJJ20gdXNpbmcgbG9naXN0aWMgcmVncmVzc2lvbiBvbiByZXNvdXJjZS1ncm91cCBhc3NvY2lhdGlvbnMsIHdpdGggb3V0Y29tZSAocHJlZGljdGVkKSB2YXJpYWJsZSBiZWluZyB0aGUgZmluZ2VycHJpbnRpbmcgc2NvcmUgY2FsY3VsYXRlZCBieSB0cmFja2VyLXJhZGFyLiBJIHdhbnQgdG8gc2VlIHdoaWNoIGNhbGwgZ3JvdXBzLCBvciB0aGVpciBjb21iaW5hdGlvbnMgYXJlIGFzc29jaWF0ZWQgd2l0aCBoaWdoIGZpbmdlcnByaW50aW5nIHNjb3JlLgoKT2YgY291cnNlLCB0aGUgZmluZ2VycHJpbnRpbmcgc2NvcmUgaXRzZWxmIGlzIGp1c3QgYSBoZXVyaXN0aWMgYW5kIG1pZ2h0IG5vdCBiZSBhbHdheXMgY29ycmVjdC4gQnV0IHRoaXMgaXMgb2sgZm9yIGluaXRpYWwgZXhwbG9yYXRvcnkgYW5hbHlzaXMgdG8gZGlzY292ZXIgaW50ZXJlc3RpbmcgaHlwb3RoZXNlcy4gVG8gbWFrZSB0aGluZ3MgZWFzaWVyIGZvciBpbnRlcnByZXRhdGlvbiwgSSBoYXZlIG1hZGUgdHdvIHNpbXBsaWZpY2F0aW9uczoKCiogSSBoYXZlIGJpbmFyaXplZCBhc3NvY2lhdGlvbnMgYmV0d2VlbiBjYWxsIGdyb3VwcyBhbmQgc2NyaXB0IHJlc291cmNlcy4gQ2FsbCBncm91cCBpcyBhc3NvY2lhdGVkIHdpdGggYSBnaXZlbiBzY3JpcHQgcmVzb3VyY2UsIGlmIGl0cyBhc3NvY2lhdGlvbiBwcm9iYWJpbGl0eSB3aXRoIGdpdmVuIGdyb3VwIGlzIGdyZWF0ZXIgdGhhbiB3aGF0IHdvdWxkIGJlIGV4cGVjdGVkIGluIDcwJSBvZiBjYXNlcy4KKiBJbnN0ZWFkIG9mIHByZWRpY3RpbmcgZmluZ2VycHJpbnRpbmcgc2NvcmUgb2YgMSAtIDMsIEknbSBwcmVkaWN0aW5nIGJpbmFyeSBvdXRjb21lIG9mIGZpbmdlcnByaW50aW5nIHNjb3JlIGJlaW5nIDEgdnMgMiBvciAzLgoKUmVtZW1iZXIgdGhhdCBJJ20gbm90IHVzaW5nIGNsYXNzaWZpZXIgaGVyZSB0byBhY2N1cmF0ZWx5IHByZWRpY3QgdGhlIGZpbmdlcnByaW50aW5nIHNjb3JlLiBUaGUgbWFpbiBwdXJwb3NlIGlzIHRvIHNlZSB3aGljaCBjYWxsIGdyb3VwcyBvciBjYWxsIGdyb3VwIGNvbWJpbmF0aW9ucyBtaWdodCBiZSBhc3NvY2lhdGVkIHdpdGggaGlnaCBmaW5nZXJwcmludGluZyBzY29yZXMgYW5kIHRoZXJlZm9yZSBsaWtlbHkgYXNzb2NpYXRlZCB3aXRoIHRyYWNrZXJzL2ZpbmdlcnByaW50aW5nLiBJJ20gYWxzbyBub3QgZG9pbmcgdGhlIG1vcmUgY29tbW9uIHRyYWluLXRlc3QtdmFsaWRhdGlvbiBzcGxpdCwgYnV0IHVzaW5nIGluZm9ybWF0aW9uIGNyaXRlcmlvbiAoQUlDKSBmb3IgbW9kZWwgc2VsZWN0aW9uLiBJJ20gbm90IGludGVyZXN0ZWQgaW4gaG93IGV4YWN0bHkgdGhpcyBtb2RlbCB3b3VsZCB3b3JrIGluIHByb2R1Y3Rpb24gLSBJJ20ganVzdCBpbnRlcmVzdGVkIGluIGNob29zaW5nIGJldHRlciBtb2RlbC4KCllvdSBjYW4gc2VlIHRoZSBleGNlcnB0IG9mIGRhdGEgdXNlZCBmb3IgdHJhaW5pbmcgYmVsb3c6CmBgYHtyfQphc3NvY190aHJlc2hvbGRzIDwtIGNvbFF1YW50aWxlcyh3aW5uaW5nX2xkYUBnYW1tYSxwcm9icyA9IDAuNykKYmluYXJ5X2FjdGl2YXRpb25zIDwtIHdpbm5pbmdfbGRhQGdhbW1hID4gYXNzb2NfdGhyZXNob2xkcwpiaW5hcnlfYWN0aXZhdGlvbnNfZGYgPC0gYXMuZGF0YS5mcmFtZShiaW5hcnlfYWN0aXZhdGlvbnMpCmJpbmFyeV9hY3RpdmF0aW9uc19kZiRvdXRjb21lIDwtIGNhbGxfY291bnRzJGZpbmdlcnByaW50aW5nID4gMQoKaGVhZChiaW5hcnlfYWN0aXZhdGlvbnNfZGYpCmBgYAoKSSBoYXZlIHRyaWVkIDIgdmVyc2lvbnMgb2YgbG9naXN0aWMgcmVncmVzc2lvbjoKCiogUmVncmVzc2luZyBvbiBlYWNoIHZhcmlhYmxlLCBidXQgbm90IG9uIHRoZWlyIGNvbWJpbmF0aW9ucwoqIFJlZ3Jlc3Npb24gb24gZWFjaCB2YXJpYWJsZSBhbmQgZWFjaCBwYWlyd2lzZSB2YXJpYWJsZSBjb21iaW5hdGlvbiAocGFpciBpbnRlcmFjdGlvbnMpCgpUaGUgY29kZSBiZWxvdyBmaXRzIHRoZXNlIG1vZGVscyBhbmQgb3V0cHV0cyB0aGVpciBBSUMgc2NvcmUgKGxvd2VyIGlzIGJldHRlcikuCgpgYGB7cn0KcHJlZF9tb2RlbF9iYXNlIDwtIGdsbShvdXRjb21lIH4gVjEgKyBWMiArIFYzICsgVjQsYmluYXJ5X2FjdGl2YXRpb25zX2RmLGZhbWlseSA9ICJiaW5vbWlhbCIpCnByZWRfbW9kZWxfcGFpcnMgPC0gZ2xtKG91dGNvbWUgfiBWMSArIFYyICsgVjMgKyBWNCArIFYxKlYyICsgVjEqVjMgKyBWMSpWNCArIFYyKlYzICsgVjIqVjQgKyBWMypWNCxiaW5hcnlfYWN0aXZhdGlvbnNfZGYsZmFtaWx5ID0gImJpbm9taWFsIikKZGF0YS5mcmFtZShtb2RlbD1jKCJCYXNlbGluZSBNb2RlbCIsIkludGVyYWN0aW9uIE1vZGVsIiksQUlDID0gYyhBSUMocHJlZF9tb2RlbF9iYXNlKSwKQUlDKHByZWRfbW9kZWxfcGFpcnMpKSkKYGBgCgoKSW50ZXJhY3Rpb24gbW9kZWwgaXMgYSBjbGVhciB3aW5uZXIuIFRoZSBkaWZmZXJlbmNlIGluIEFJQyB2YWx1ZSBtaWdodCBzZWVtIHNtYWxsIGF0IHRoZSBmaXJzdCBzaWdodC4gSG93ZXZlciwgQUlDIGlzIGRlZmluZWQgb24gYSBsb2dhcml0aG1pYyBzY2FsZSwgc28gdGhpcyBkaWZmZXJlbmNlIGlzIGFjdHVhbGx5IHZlcnkgc2lnbmlmaWNhbnQuIE5leHQsIEkgaGF2ZSBnZW5lcmF0ZWQgYWxsIHBvc3NpYmxlIGNvbWJpbmF0aW9ucyBvZiBhY3RpdmF0aW9ucyB0byBzZWUgd2hpY2ggd2lsbCBiZSBwcmVkaWN0ZWQgYnkgdGhlIG1vZGVsIHRvIGhhdmUgbW9yZSB0aGFuIDUwJSBwcm9iYWJpbGl0eSBvZiB0cmFja2luZyBiZWhhdmlvci4gCgpUaGUgdGFibGUgYmVsb3cgc2hvd3MgdGhlIGNhbGwgZ3JvdXAgY29tYmluYXRpb25zIGFzc29jaWF0ZWQgd2l0aCB0cmFja2luZy9maW5nZXJwcmludGluZyBiZWhhdmlvciAocD41MCUpIG9yZGVyZWQgZnJvbSBtb3N0IHByb2JhYmxlIHRvIHRoZSBsZWFzdCBwcm9iYWJsZS4gQWNyb255bSBmb3IgZWFjaCBjb21iaW5hdGlvbiB3YXMgY3JlYXRlZCBieSB0YWtpbmcgZmlyc3QgbGV0dGVycyBvZiBlYWNoIGNhbGwgZ3JvdXAgdGFodCBpdCBjb25zaXN0cyBvZi4gCgoKYGBge3J9CmFsbF9jb21icyA8LSBleHBhbmQuZ3JpZChWMT1jKFQsRiksVjI9YyhULEYpLFYzPWMoVCxGKSwgVjQgPSBjKFQsRikpCmFsbF9jb21icyRQcm9iYWJpbGl0eSA8LSBwcmVkaWN0KHByZWRfbW9kZWxfcGFpcnMsYWxsX2NvbWJzLHR5cGU9InJlc3BvbnNlIikKCgphbGxfY29tYnMgJT4lCiAgZmlsdGVyKFByb2JhYmlsaXR5ID4gMC41KSAlPiUKICBhcnJhbmdlKGRlc2MoUHJvYmFiaWxpdHkpKSAlPiUKICBtdXRhdGUoCiAgICBBY3JvbnltID0gcGFzdGUwKGlmZWxzZShWMSwiSSIsIiIpLGlmZWxzZShWMiwiQlAiLCIiKSxpZmVsc2UoVjMsIlIiLCIiKSxpZmVsc2UoVjQsIkMiLCIiKSksCiAgICBWMSA9IGlmZWxzZShWMSwiSWRlbnRpdHkmUGVyc2lzdGFuY2UiLCIiKSwKICAgIFYyID0gaWZlbHNlKFYyLCJCcm93c2VyIFBlcnNvbmFsaXR5IiwiIiksCiAgICBWMyA9IGlmZWxzZShWMywiUmVuZGVyaW5nJlZpZXdwb3J0IiwiIiksCiAgICBWNCA9IGlmZWxzZShWNCwiQ2FwYWJpbGl0eSIsIiIpCiAgKSAlPiUKICBzZWxlY3QoQWNyb255bSxWMSxWMixWMyxWNCxQcm9iYWJpbGl0eSkKYGBgCgoKIyMjIEFuYWx5emluZyBTdXNwZWN0ZWQgVHJhY2tlciBQYXR0ZXJucyBpbiBUaW1lCgpIYXZpbmcgZXN0YWJsaXNoZWQgdGhlIEFQSSBjYWxsIGdyb3VwcyBhbmQgdGhlaXIgY29tYmluYXRpb25zIGFzc29jaWF0ZWQgd2l0aCBmaW5nZXJwcmludGluZywgSSBjYW4gbm93IHVzZSB0aGVzZSBjb21iaW5hdGlvbnMgdG8gc2VlIGhvdyB0aGUgY2hhcmFjdGVyIG9mIGZpbmdlcnByaW50aW5nIGV2b2x2ZWQgaW4gdGltZS4gRmlyc3QsIEknbGwgaW52ZXN0aWdhdGUgb3ZlcmFsbCBkZXZlbG9wbWVudCBhbmQgdGhlbiBJJ2xsIGludmVzdGlnYXRlIHRoZSBiaWdnZXN0IHBsYXllcnMgaW4gdHJhY2tpbmcuCgpJbiBvcmRlciB0byBkbyB0aGF0LCBJIGhhZCB0byBkbyBzb21lIGRhdGEgcHJvY2Vzc2luZyBmb2N1c2VkIG9uIGFubm90YXRpb24gb2YgZWFjaCBzY3JpcHQgcmVzb3VyY2UgYnkgdGhlIGNhbGwgZ3JvdXAgY29tYmluYXRpb24gaXQgYmVsb25ncyB0by4KCmBgYHtyfQpjYWxsX2NvdW50c19pbl90aW1lIDwtYXBpX2NhbGxzX2luX3RpbWUgJT4lCiAgcGl2b3Rfd2lkZXIobmFtZXNfZnJvbT1hcGksdmFsdWVzX2Zyb20gPSBjYWxscyx2YWx1ZXNfZmlsbCA9IDApIAoKY2FsbF9jb3VudHNfbWF0X2luX3RpbWUgPC0gY2FsbF9jb3VudHNfaW5fdGltZSAlPiUgCiAgc2VsZWN0KC1jKGRhdGUscmVnaW9uLGRvbWFpbixydWxlLGZpbmdlcnByaW50aW5nKSkgJT4lIAogIGFzLm1hdHJpeCgpCgojT3JkZXIgY29sdW1ucyBpbiB0aGUgc2FtZSB3YXkgYXMgaW4gTERBIHRyYWluaW5nCmNhbGxfY291bnRzX21hdF9pbl90aW1lIDwtIGNhbGxfY291bnRzX21hdF9pbl90aW1lWyxjb2xuYW1lcyhjYWxsX2NvdW50c19tYXQpXQoKbm9uX3plcm9fcm93IDwtIHJvd1N1bXMoY2FsbF9jb3VudHNfbWF0X2luX3RpbWUpID4gMApjYWxsX2NvdW50c19tYXRfaW5fdGltZSA8LSBjYWxsX2NvdW50c19tYXRfaW5fdGltZVtub25femVyb19yb3csXQpjYWxsX2NvdW50c19pbl90aW1lX25vbnplcm8gPC0gY2FsbF9jb3VudHNfaW5fdGltZVtub25femVyb19yb3csXQoKdG9waWNfcG9zdGVyaW9ycyA8LSBwb3N0ZXJpb3Iod2lubmluZ19sZGEsY2FsbF9jb3VudHNfbWF0X2luX3RpbWUpCmFjdGl2YXRpb25zX2luX3RpbWUgPC0gdG9waWNfcG9zdGVyaW9ycyR0b3BpY3MgPiBhc3NvY190aHJlc2hvbGRzCmNvbG5hbWVzKGFjdGl2YXRpb25zX2luX3RpbWUpIDwtIGMoIlYxIiwiVjIiLCJWMyIsIlY0IikKCnRyYWNrZXJfdHlwZXNfaW5fdGltZSA8LSBhY3RpdmF0aW9uc19pbl90aW1lICU+JSBhcy5kYXRhLmZyYW1lICU+JQogIG11dGF0ZSgKICAgIHRyYWNrZXJfdHlwZSA9IGNhc2Vfd2hlbigKICAgICAgVjEgJiBWMiAmIFYzICYgVjQgfiAiSUJQUkMiLCAgICMgSWRlbnRpdHkgKyBCcm93c2VyIFBlcnNvbmFsaXR5ICsgUmVuZGVyaW5nICsgQ2FwYWJpbGl0eQogICAgICBWMiAmIFYzICYgVjQgICAgICB+ICJCUFJDIiwgICAgIyBCcm93c2VyIFBlcnNvbmFsaXR5ICsgUmVuZGVyaW5nICsgQ2FwYWJpbGl0eQogICAgICBWMiAmIFY0ICAgICAgICAgICB+ICJCUEMiLCAgICAgIyBCcm93c2VyIFBlcnNvbmFsaXR5ICsgQ2FwYWJpbGl0eQogICAgICBWMyAmIFY0ICAgICAgICAgICB+ICJSQyIsICAgICAgIyBSZW5kZXJpbmcgKyBDYXBhYmlsaXR5CiAgICAgIFY0ICAgICAgICAgICAgICAgIH4gIkMiLCAgICAgICAjIENhcGFiaWxpdHkgb25seQogICAgICBWMSAmIFYyICYgVjMgICAgICB+ICJJQlBSIiwgICAgIyBJZGVudGl0eSArIEJyb3dzZXIgUGVyc29uYWxpdHkgKyBSZW5kZXJpbmcKICAgICAgVjEgJiBWMiAmIFY0ICAgICAgfiAiSUJQQyIsICAgICMgSWRlbnRpdHkgKyBCcm93c2VyIFBlcnNvbmFsaXR5ICsgQ2FwYWJpbGl0eQogICAgICBWMSAmIFYzICYgVjQgICAgICB+ICJJUkMiLCAgICAgIyBJZGVudGl0eSArIFJlbmRlcmluZyArIENhcGFiaWxpdHkKICAgICAgVFJVRSAgICAgICAgICAgICAgfiBOQV9jaGFyYWN0ZXJfCiAgICApCiAgKSAKCnRyYWNrZXJfdHlwZXNfaW5fdGltZSRkb21haW4gPC0gY2FsbF9jb3VudHNfaW5fdGltZV9ub256ZXJvJGRvbWFpbgp0cmFja2VyX3R5cGVzX2luX3RpbWUkZGF0ZSA8LSBjYWxsX2NvdW50c19pbl90aW1lX25vbnplcm8kZGF0ZQoKCnRyYWNrZXJfdHlwZXNfZmlsdGVyZWRfaW5fdGltZSA8LSB0cmFja2VyX3R5cGVzX2luX3RpbWUgJT4lCiAgZmlsdGVyKCFpcy5uYSh0cmFja2VyX3R5cGUpKQpgYGAKCgpUaGUgcGxvdCBiZWxvdyBzaG93cyBob3cgdGhlIG51bWJlciBvZiBzY3JpcHRzIGFzc29jaWF0ZWQgd2l0aCBlYWNoIHBhdHRlcm4gZXZvbHZlZCBvdmVyIHRpbWUuIFRoZXJlIGFyZSBhIGNvdXBsZSBvZiB0aGluZ3MgdG8gbm90ZSBoZXJlOgoKKiBPdXQgb2YgdGhlIDEwIHBhdHRlcm5zIHN1Z2dlc3RpbmcgdHJhY2tpbmcgYWNjb3JkaW5nIHRvIHRoZSBsb2dpc3RpYyByZWdyZXNzaW9uIG1vZGVsLCBvbmx5IDUgYXJlIGFjdHVhbGx5IHJlYWxseSBvY2N1cnJpbmcgaW4gdGhlIHdpbGQuIFRoaXMgaXMgZXhwZWN0ZWQgYmVoYXZpb3IgLSBzdGF0aXN0aWNhbCBtb2RlbHMgY2FuIGJlIHVzZWQgZm9yIHNwZWN1bGF0aW9uIG92ZXIgZmljdGlvbmFsIHNjZW5hcmlvcy4gSSBoYXZlIGxpbWl0ZWQgZnVydGhlciBhbmFseXNpcyB0byB0aGVzZSByZWFsIHBhdHRlcm5zOgogICogQlBDIC0gQnJvd3NlciBQZXJzb25hbGl0eSBhbmQgQ2FwYWJpbGl0eSAKICAqIEJQUkMgLSBCcm93c2VyIFBlcnNvbmFsaXR5LCBSZW5kZXJpbmcgYW5kIENhcGFiaWxpdHkgCiAgKiBDIC0gQ2FwYWJpbGl0eQogICogSUJQUiAtIElkZW50aXR5LCBCcm93c2VyIFBlcnNvbmFsaXR5IGFuZCBSZW5kZXJpbmcmVmlld3BvcnQKKiBUaGUgcGxvdCBjbGVhcmx5IHNob3dzIHRoYXQgdGhlIHByZWRvbWluYW50IHBhdHRlcm4gaXMgQ2FwYWJpbGl0eS1vbmx5IGZvbGxvd2VkIGJ5IHRoZSBjb21iaW5hdGlvbiBvZiBCcm93c2VyIFBlcnNvbmFpdHkgYW5kIENhcGFiaWxpdHkuCiogSG93ZXZlciwgYXQgc29tZSB0aW1lICoqYmV0d2VlbiAyMDIyIGFuZCAyMDIzIHRoZXJlJ3MgYSBzdWRkZW4gdXB0aWNrIGluIHR3byBwYXR0ZXJucyB3aXRoIGZ1bmN0aW9ucyBrbm93biB0byBiZSBpbnZvbHZlZCBpbiAgdmlld3BvcnQgZmluZ2VycHJpbnRpbmcgLSBJQlBSIGFuZCBlc3BlY2lhbGx5IFJDKiouCgpgYGB7ciwgZmlnLndpZHRoPTEwfQpsaWJyYXJ5KGdncGxvdDIpCnRyYWNrZXJfdHlwZXNfZmlsdGVyZWRfaW5fdGltZSAlPiUKICBncm91cF9ieShkYXRlLHRyYWNrZXJfdHlwZSkgJT4lCiAgc3VtbWFyaXNlKG49bigpKSAlPiUKICBnZ3Bsb3QoYWVzKHg9ZGF0ZSx5PW4sZmlsbD10cmFja2VyX3R5cGUpKSArIAogIGdlb21fYXJlYSgpICsKICB5bGFiKCJOdW1iZXIgb2YgU2NyaXB0IFJlc291cmNlcyIpICsKICB4bGFiKCJUaW1lIikgKwogIGxhYnMoZmlsbD0iU3VzcGVjdGVkIFRyYWNrZXIgVHlwZSIpICsKICBnZ3RpdGxlKCJFdm9sdXRpb24gb2YgU3VzcGVjdGVkIFRyYWNrZXIgUGF0dGVybiBQb3B1bGFyaXR5IGluIFRpbWUiKQpgYGAKClRoZSBwbG90IGJlbG93IHNob3dzIHRoZSBzYW1lIGV2b2x1dGlvbiBvZiBzdXNwZWN0ZWQgdHJhY2tlciBwYXR0ZXJucyBpbiBib3RoIGFic29sdXRlIGFuZCByZWxhdGl2ZSB0ZXJtcyAocGVyY2VudGFnZXMpIGJyb2tlbiBkb3duIGZvciBlYWNoIG1ham9yIHBsYXllci4KSXQgaXMgYXBwYXJlbnQgdGhhdCB0aGUgc2FtZSBwYXR0ZXJuIHNlZW1zIHRvIGhvbGQgd2l0aCBuZWFybHkgYWxsIG1ham9yIHBsYXllcnMuIEVzcGVjaWFsbHkgR29vZ2xlLCBBa2FtYWkgYW5kIEFkb2JlIHNlZW0gdG8gaGF2ZSBzdWRkZW4gdXB0aWNrIGluIElCUFIgYW5kIFJDLCB3aGlsZSBNaWNyb3NvZnQgc2VlbXMgdG8gYWx3YXlzIGhhdmUgaGFkIGEgc2lnbmlmaWNhbnQgcGFydCBvZiBpdHMgcmVzb3VyY2VzIGFsaWduZWQgd2l0aCB0aGUgUkMgcGF0dGVybi4KCmBgYHtyLCBmaWcuaGVpZ2h0PTE1fQpsaWJyYXJ5KHBhdGNod29yaykKCmRvbWFpbl9vd25lcnNfaW5fdGltZSA8LSByZWFkLmNzdigiL1VzZXJzL3N0YW5pc2xhdi5zbWF0YW5hL0RvY3VtZW50cy9QZXJzb25hbCBQcm9qZWN0cy90cmFja2VyLXJhZGFyL293bmVyX2RvbWFpbnNfYnlfcmVsZWFzZS5jc3YiKSAlPiUKICBzZXBhcmF0ZShyZWxlYXNlX3RhZywgaW50byA9IGMoInllYXIiLCAibW9udGgiLCAiZGF5IiksIHNlcCA9ICJcXC4iLCBmaWxsID0gInJpZ2h0IikgJT4lCiAgbXV0YXRlKAogICAgbW9udGggPSBzcHJpbnRmKCIlMDJkIiwgYXMuaW50ZWdlcihtb250aCkpLAogICAgZGF5ICAgPSBpZmVsc2UoaXMubmEoZGF5KSwgIjAxIiwgc3ByaW50ZigiJTAyZCIsIGFzLmludGVnZXIoZGF5KSkpLAogICAgZGF0ZSAgPSBhcy5EYXRlKHBhc3RlKHllYXIsIG1vbnRoLCBkYXksIHNlcCA9ICItIikpCiAgKQoKdG9wX293bmVyc19pbl90aW1lIDwtIGRvbWFpbl9vd25lcnNfaW5fdGltZSAlPiUgCiAgZ3JvdXBfYnkoZGF0ZSxvd25lcikgJT4lCiAgc3VtbWFyaXNlKG49bigpKSAlPiUKICB1bmdyb3VwKCkgJT4lCiAgZ3JvdXBfYnkoZGF0ZSkgJT4lCiAgdG9wX24oMykgJT4lCiAgYCRgKG93bmVyKSAlPiUKICB1bmlxdWUKCmdpZ2FudHNfaW5fdGltZSA8LSB0cmFja2VyX3R5cGVzX2ZpbHRlcmVkX2luX3RpbWUgJT4lCiAgaW5uZXJfam9pbihkb21haW5fb3duZXJzX2luX3RpbWUgJT4lIGZpbHRlcihvd25lciAlaW4lIHRvcF9vd25lcnNfaW5fdGltZSkpICU+JQogIGdyb3VwX2J5KGRhdGUsb3duZXIsdHJhY2tlcl90eXBlKSAlPiUKICBzdW1tYXJpemUobj1uKCkpCgphYnNvbHV0ZSA8LSBnaWdhbnRzX2luX3RpbWUgJT4lCiAgZ2dwbG90KGFlcyh4PWRhdGUseT1uLGZpbGw9dHJhY2tlcl90eXBlKSkgKwogIGdlb21fYXJlYSgpICsKICBmYWNldF93cmFwKH5vd25lcikgKyAKICB4bGFiKCJUaW1lIikgKyAKICB5bGFiKCJOdW1iZXIgb2YgU2NyaXB0IFJlc291cmNlcyIpICsKICBnZ3RpdGxlKCJBYnNvbHV0ZSBEZXZlbG9wbWVudCBvZiBTdXNwZWN0ZWQgVHJhY2tlciBUeXBlcyBpbiBUaW1lIikKCgpyZWxhdGl2ZSA8LSBnaWdhbnRzX2luX3RpbWUgJT4lCiAgZ2dwbG90KGFlcyh4PWRhdGUseT1uLGZpbGw9dHJhY2tlcl90eXBlKSkgKwogIGdlb21fYXJlYShwb3NpdGlvbj0iZmlsbCIpICsKICBmYWNldF93cmFwKH5vd25lcikgKyAKICB4bGFiKCJUaW1lIikgKyAKICB5bGFiKCJQcm9wb3J0aW9uIG9mIFNjcmlwdCBSZXNvdXJjZXMiKSArCiAgZ2d0aXRsZSgiUmVsYXRpdmUgRGV2ZWxvcG1lbnQgb2YgU3VzcGVjdGVkIFRyYWNrZXIgVHlwZXMgaW4gVGltZSIpCgphYnNvbHV0ZSAvIHJlbGF0aXZlCgoKCmBgYApGaW5hbGx5LCBJJ3ZlIGRlY2lkZWQgdG8gem9vbSBpbiBpbnRvIHRoZSBncmFpbiBvZiBpbmRpdmlkdWFsIGNhbGxzLiBJIHdhbnRlZCB0byBhbnN3ZXIgdGhlIHF1ZXN0aW9uIC0gaG93IGRvIHRoZSBwcm9wb3J0aW9ucyBvZiBkaWZmZXJlbnQgQVBJIGNhbGxzIGRpZmZlciBwcmUgYW5kIHBvc3QgMjAyMj8gSW4gb3JkZXIgdG8gaWRlbnRpZnkgY2hhbmdlcyB0aGF0IGFyZSBzaWduaWZpY2FudCwgSSdtIHVzaW5nIGNoaS5zcXVhcmUgdGVzdCBvdmVyIDJ4MiBjb250aW5nZW5jeSBtYXRyaWNlcy4gVGhpcyBtZWFucyB0aGF0IGZvciBldmVyeSBjYWxsIEkgY3JlYXRlIGEgbWF0cml4IG9mIHRoZSBmb3JtCgoKYGBge3IsZWNobz1GfQpsaWJyYXJ5KGtuaXRyKQpsaWJyYXJ5KGthYmxlRXh0cmEpCgprYWJsZShkYXRhLmZyYW1lKAogICJYIiA9IGMoIkJlZm9yZSAyMiIsIkFmdGVyIDIyIiksCiAgIkNhbGxzX29mX2Z1bmN0aW9uX2YiID0gYygibl9mX3ByZTIyIiwibl9mX3Bvc3QyMiIpLAogICJPdGhlcl9jYWxscyI9IGMoIm5fb3RoZXJfcHJlMjIiLCJuX290aGVyX3Bvc3QyMiIpKSwgZm9ybWF0ID0gImh0bWwiLCB0YWJsZS5hdHRyID0gInN0eWxlPSd3aWR0aDo1MCU7JyIpICU+JSAKICBrYWJsZV9zdHlsaW5nKCkKYGBgCgpBbmQgdGhlbiBhcHBseSBjaGlzLnNxdWFyZSB0ZXN0IHRvIGRlcml2ZSBzaWduaWZpY2FuY2UgcC12YWx1ZXMuIEkndmUgY2hvc2VuIHRoZSBzaWduaWZpY2FuY2UgdGhyZXNob2xkIDAuMDEuIEhvd2V2ZXIsIGJlY2F1c2UgSSdtIHBlcmZvcm1pbmcgYSBncmVhdCBudW1iZXIgb2YgdGhlc2UgdGVzdHMsIHRoZSBzaWduaWZpY2FuY2UgdGhyZXNob2xkIG5lZWRzIHRvIGJlIGRpdmlkZWQgYnkgdGhlIG51bWJlciBvZiB0ZXN0cyBzbyB0aGF0IHJlc3VsdHMgaGF2ZSBleHBlY3RlZCBlcnJvciByYXRlICh0aGlzIGlzIGNhbGxlZCBCb25mZXJyb25pIGNvcnJlY3Rpb24pLiAgQ29tbWVudGFyeSBvZiByZXN1bHRzIGlzIHBhcnQgb2YgdGhlIG5leHQgc2VjdGlvbiAtIENvbmNsdXNpb24uCgpgYGB7cn0KcGVyaW9kX2FwaV9jYWxscyA8LSBhcGlfY2FsbHNfaW5fdGltZSAlPiUgCiAgc2VsZWN0KC1jKHJlZ2lvbixkb21haW4scnVsZSkpICU+JQogIG11dGF0ZShwcmVfMjAyMiA9IGRhdGUgPCBhcy5EYXRlKCIyMDIyLTEtMSIpKSAlPiUKICBncm91cF9ieShwcmVfMjAyMixhcGkpICU+JQogIHN1bW1hcml6ZShjYWxscyA9IHN1bShjYWxscykpCgpwZXJpb2RfYXBpX2NhbGxzX3dpZGUgPC0gcGVyaW9kX2FwaV9jYWxscyAlPiUKICBncm91cF9ieShwcmVfMjAyMikgJT4lCiAgbXV0YXRlKHBlcmlvZF90b3RhbF9jYWxscyA9IHN1bShjYWxscykpICU+JQogIHVuZ3JvdXAoKSAlPiUKICBwaXZvdF93aWRlcihuYW1lc19mcm9tID0gcHJlXzIwMjIsdmFsdWVzX2Zyb20gPSBjKGNhbGxzLHBlcmlvZF90b3RhbF9jYWxscyksdmFsdWVzX2ZpbGwgPSAwKSAlPiUKICBtdXRhdGUocGVyaW9kX3RvdGFsX2NhbGxzX1RSVUUgPSBpZmVsc2UocGVyaW9kX3RvdGFsX2NhbGxzX1RSVUUgPT0gMCxwZXJpb2RfdG90YWxfY2FsbHNfVFJVRVtwZXJpb2RfdG90YWxfY2FsbHNfVFJVRSA+IDBdWzFdLHBlcmlvZF90b3RhbF9jYWxsc19UUlVFKSkKCgpjaGlfc3F1YXJlX2NhbGxzIDwtIGZ1bmN0aW9uKGRmKXsKICBjYWxsc19wcmUyMiA8LSBkZiRjYWxsc19UUlVFCiAgbm9uX2NhbGxzX3ByZTIyIDwtIGRmJHBlcmlvZF90b3RhbF9jYWxsc19UUlVFIC0gZGYkY2FsbHNfVFJVRQogIGNhbGxzX3Bvc3QyMiA8LSBkZiRjYWxsc19GQUxTRQogIG5vbl9jYWxsc19wb3N0MjIgPC0gZGYkcGVyaW9kX3RvdGFsX2NhbGxzX0ZBTFNFIC0gZGYkY2FsbHNfRkFMU0UKICAKICBhbGxfZGF0YV9tYXQgPC0gY2JpbmQoY2FsbHNfcHJlMjIsbm9uX2NhbGxzX3ByZTIyLGNhbGxzX3Bvc3QyMixub25fY2FsbHNfcG9zdDIyKQogIGFwcGx5KGFsbF9kYXRhX21hdCwxLGZ1bmN0aW9uKHJvdyl7CiAgICBjaGlzcS50ZXN0KHJiaW5kKAogICAgICBjKHJvd1siY2FsbHNfcHJlMjIiXSxyb3dbIm5vbl9jYWxsc19wcmUyMiJdKSwKICAgICAgYyhyb3dbImNhbGxzX3Bvc3QyMiJdLHJvd1sibm9uX2NhbGxzX3Bvc3QyMiJdKQogICAgKSkkcC52YWx1ZQogIH0pCn0KCnBlcmlvZF9hcGlfY2FsbHNfd2lkZSRzaWduaWZpY2FudCA8LSBjaGlfc3F1YXJlX2NhbGxzKHBlcmlvZF9hcGlfY2FsbHNfd2lkZSkgPCA3Ljg3NDAxNmUtMDUgI2JvbmZlcm9uaSBjb3JyZWN0aW9uCnBlcmlvZF9hcGlfY2FsbHNfd2lkZSAlPiUgCiAgZmlsdGVyKHNpZ25pZmljYW50KSAlPiUKICBzZWxlY3QoYXBpLGNhbGxzX1RSVUUsY2FsbHNfRkFMU0UpICU+JQogIGFycmFuZ2UoY2FsbHNfVFJVRSkgJT4lCiAgcmVuYW1lKGNhbGxzX2JlZm9yZV8yMDIyID0gY2FsbHNfVFJVRSxjYWxsc19hZnRlcl8yMDIyID0gY2FsbHNfRkFMU0UpCgpgYGAKCiMjIyBDb25jbHVzaW9uCgpJdCBkb2VzIHNlZW0gbGlrZSBvbiB0b3Agb2YgdGhlIHNoaWZ0IGluIGNhbGwgZ3JvdXBzLCB0aGVyZSBpcyBhIHNpZ25pZmljYW50IGRpZmZlcmVuY2UgaW4gcHJvcG9ydGlvbnMgYmVmb3JlIDIwMjIgYW5kIGFmdGVyIDIwMjIgZm9yIHByZXR0eSBtdWNoIGV2ZXJ5IGluZGl2aWR1YWwgQVBJIGNhbGwuIFdoYXQncyBtb3JlIGltcG9ydGFudCBzb21lIGhlYXZpbHkgdXNlZCBBUEkgY2FsbHMgYXJlIGNvbXBsZXRlbHkgYWJzZW50IGJlZm9yZSAyMDIyLiBUaGlzIGxlYWRzIG1lIHRvIGEgY291cGxlIG9mIGh5cG90aGVzaXMgZXhwbGFpbmluZyB0aGUgc3VkZGVuIHNoaWZ0IG9mIHRyYWNraW5nIHBhdHRlcm5zIGluIDIwMjI6CgoqIFRoZXJlIHdhcyBhIHRydWUgY2hhbmdlIG9mIGhvdyBjb21wYW5pZXMgYXJlIGRvaW5nIGZpbmdlcnByaW50aW5nIG1vdGl2YXRlZCBieSBuZXcgYXBwcm9hY2hlcyBvciBjaGFuZ2UgaW4gKGxlZ2lzbGF0aXZlPykgZW52aXJvbm1lbnQuCiogU29tZSBBUEkgY2FsbHMgdXNlZnVsIGZvciBmaW5nZXJwcmludGluZyB3ZXJlIG5vdCBhdmFpbGFibGUgaW4gSlMgYmVmb3JlIDIwMjIuIFRoZWlyIGFkZGl0aW9uIG1vdGl2YXRlZCBjb21wYW5pZXMgdG8gaW5jbHVkZSB0aGVtIGluIHRoZWlyIHRyYWNraW5nIGFwcHJvYWNoZXMuCiogVGhlIHdheSB0cmFja2VyLXJhZGFyIGNvbGxlY3RzIGRhdGEgaGF2ZSBjaGFuZ2VkIGFuZCB0aGVyZSdzIG5vIHJlYWwgY2hhbmdlIGluIGJlaGF2aW9yIG9mIG9yZ2FuaXphdGlvbnMgdGhhdCBkbyBmaW5nZXJwcmludGluZy90cmFja2luZy4KKiBPciBhbnkgY29tYmluYXRpb24gb2YgYWJvdmUuCgpJIGhhdmUgYSBxdWVzdGlvbiBmb3IgeW91LCBkZWFyIHJlYWRlciwgd2hhdCBkbyB5b3UgdGhpbmsgaXMgdGhlIG1vc3QgbGlrZWx5IGV4cGxhbmF0aW9uPyBGZWVsIGZyZWUgdG8gc2hhcmUgYW55IGlkZWFzIHlvdSBoYXZlIG9uIG15IFtsaW5rZWRpbl0oaHR0cHM6Ly93d3cubGlua2VkaW4uY29tL2luL3N0YW5pc2xhdnNtYXRhbmEvKSBvciB2aWEgbXkgZS1tYWlsIHN0YW5pc2xhdnNtYXRhbmFAZ21haWwuY29tLgo=