1 Motivation

1.1 Brain imaging study

  • Diffusion Tensor Imaging (DTI) data
  • DTI measures fluid flows in the brain
  • Comparing brain activity of six dyslexic children versus six normal controls
  • From each child, DTI produced observations on 15443 voxels (voxel = small volume at a particular (x, y, x) coordinate)
  • For each voxel, a two-sided two-sample t-test has been performed, resulting in a z-value (15443 z-values) for fractional anisotropy.
  • Low values for FA indicate diffusion in all directions, high values indicates directional diffusion.
  • Research question: at what brain locations (voxels) show dyslexic children a different brain activity as compared to children without dyslexia?

For each voxel separately, this is a simple problem, but the large scale of the problem (15443 simultaneous hypothesis tests) causes the problem of multiplicity.

1.1.1 Data Exploration

The dataset dti contains

  • Spatial location (x, y, z) of each voxel
  • z-statistic for assessing differential brain activity between dyslexic and non-dyslexic children
library(tidyverse)
library(locfdr)
library(gganimate)

dti <- read_csv("https://raw.githubusercontent.com/statOmics/HDA2020/data/dti.csv",
                col_types = cols())
pZ <- dti %>%
  ggplot(
    aes(
      coord.y,
      coord.x,
      color=z.value)
    ) +
  geom_point() +
  scale_colour_gradient2(low = "blue",mid="white",high="red") +
  transition_manual(coord.z) +
  labs(title = "transection z = {frame}") +
  theme_grey()

We will now plot the animated graph

pZ

WARNING: The animated graph will only be visible in the HTML output, not in PDF format. If you’re reading the PDF version, check online for the animated graph.

We visualised the test-statistic of each test per voxel!

Note, that it is difficult to see structure in the data.

1.1.2 Inference

We can convert the z-statistic in a two-sided p-value for each voxel to assess

\[H_0: \text{There is on average no difference in brain activity in voxel xyz between dyslexic and non-dyslexic children}\] \[\mu_d=\mu_{nd}\]

vs

\[H_0: \text{There is on average a difference in brain activity in voxel xyz between dyslexic and non-dyslexic children}\] \[\mu_d\neq\mu_{nd}\]

Below, we calculate the p-values and a variable zP for which we keep the z-value if it is statistical significant at the 5% level otherwise we set it equal to zP=0.

dti <- dti %>%
  mutate(
    p.value = pnorm(abs(z.value),lower=FALSE)*2,
    zP = (p.value < 0.05) * z.value)

pPval <- dti %>%
  ggplot(
    aes(
      coord.y,
      coord.x,
      color=zP)
    ) +
  geom_point() +
  scale_colour_gradient2(low = "blue",mid="white",high="red") +
  transition_manual(coord.z) +
  labs(title = "transection z = {frame}") +
  theme_grey()

We will now plot the animated graph

pPval

It is much more easy to observe patterns of activity.

Note, however that

  • Higher average FA (z > 0 and p < 0.05) in dyslexic children is appearing in spatial patterns in some locations.
  • Lower average FA (z < 0 and p > 0.05) in dyslexic children is scattered throughout the brain.
  • Multiple testing problem.
  • If there would be no association between brain activity and dyslexia we can expect on average 772.15 false positive voxels.
  • Note, that only 1241 were significant at the 5% significance level, so we can expect that the majority of the returned voxels are false positives.
FPexpected  <- nrow(dti) * 0.05
Preported <- sum(dti$p.value < 0.05)

FPexpected
## [1] 772.15
Preported
## [1] 1241

1.2 Challenges

Large Scale Inference implies

  • Many hypothesis to be evaluated
  • Huge multiple testing problem
  • Many false positives can be expected if we do not correct for multiple testing

Issue is widespread in many disciplines

  • genomics
  • transcriptomics
  • proteomics
  • brain imaging
  • high throughput single cell technologies
  • detection of anomalous events: e.g. credit card fraud
  • evaluation of trading rules
  • academic performance of schools

1.3 Multiplicity Problem

Suppose only a single hypothesis test is required for answering the research question. A statistical test controls the probability of making a type I error (type I error rate), \[ \alpha =\text{P}\left[\text{reject }H_0 \mid H_0\right] . \] The type I error is also known as a false positive (i.e. \(H_0\) expresses an negative result, and \(H_1\) a positive result): \(\alpha=\text{P}\left[\text{false positive}\right]\).

An important property:

When \(H_0\) is true, and the assumptions underlying the test hold true, then \[ P \sim U[0,1] . \] Hence, for any \(0<\alpha<1\), \[ \text{P}\left[\text{reject }H_0 \mid H_0\right] = \text{P}\left[P<\alpha \mid H_0\right] = \alpha. \]

The distribution of the z-statistic and the p-values under \(H_0\) are illustrated below:

library(gridExtra)

simData <- tibble(
  z.value = rnorm(20000)
  )

simData <- simData %>% mutate(p.value = 2*(1-pnorm(abs(z.value))))

p1 <- simData %>%
  ggplot(aes(x = z.value)) +
  geom_histogram(
    aes(y=..density..),
    color = "black") +
  stat_function(fun = dnorm, args=list(mean=0, sd=1))

p2 <- simData %>%
  ggplot(aes(x = p.value)) +
  geom_histogram(color = "black", breaks = seq(0,1,.05))

grid.arrange(p1, p2, ncol=2)
## `stat_bin()` using `bins = 30`. Pick better value with `binwidth`.

We indeed observe that the p-values are uniform under the null hypothesis. So statistical hypothesis testing provides a uniform testing strategy.

1.3.1 Notation

In the multiple testing literature the number of features that for which a test is conducted is denoted by \(m\) instead of \(p\) to avoid confusion with the symbol for a p-value.

1.3.2 Familywise error rate

Suppose that \(m\) hypotheses have to be tested simultaneously for answering a single research question.

Let \(H_{0i}\) denote the \(i\)th null hypothesis (\(i=1,\ldots, m\)) and let \(H_0\) denote the intersection of all these partial null hypotheses.

In this case the type I error rate is no longer relevant. Instead one may consider the Familywise Error Rate (FWER) \[ \text{FWER}=\text{P}\left[\text{reject at least one }H_{0i} \mid H_0\right]. \]

Assuming independence among the \(m\) tests and assuming that all individual tests are performed at the \(\alpha\) level of significance, the FWER can be computed as

\[ \begin{array}{rcl} \text{FWER} &=& \text{P}\left[\text{reject at least one }H_{0i} \mid H_0\right] \\ &=& 1 - \text{P}\left[\text{reject no }H_{0i} \mid H_0\right] \\ &=& 1- \text{P}\left[\text{not reject }H_{01}\text{ and }\ldots\text{ and not reject }H_{0m} \mid H_0\right] \\ &=& 1- \prod_{i=1}^m \text{P}\left[\text{not reject }H_{0i} \mid H_0\right] \\ &=& 1- (1-\alpha)^m . \end{array} \]

Examples:

\(\alpha=0.05\) and \(m=5\): FWER\(=0.23\)

\(\alpha=0.05\) and \(m=100\): FWER\(=0.99\)

\(\alpha=0.05\) and \(m=15443\): FWER\(\approx 1\).


These calculations illustrate the problem of multiplicity: the more tests that are performed, the larger the probability that at least one false positive conclusion is obtained. Thus if all significant results are listed, and suppose that all null hypotheses hold true, then the FWER is the probability that at least one of the listed positive results is a false positive. Sometimes, a list of significant results represent the “discoveries” from the study, and therefore a false positive result is often also referred to as a false discovery.

For example, with \(m=100\) and \(\alpha=0.05\) the chance that at least one of the “discoveries” is false, is about \(99\%\). Even worse, with \(m\approx 15000\) the FWER increases to virtually \(100\%\). In general we also expect that lists of significant results (discoveries) get longer with increasing \(m\).

Many researchers, however, when presented a long list of significant results (or discoveries), would not mind too much if one or a few false discoveries appear in the list. Hence, the FWER is not the most relevant risk measure, as the FWER is allowed to be \(100\%\) in case researchers do not mind to have a few false discoveries among the (perhaps many) positive results in the list of discoveries. A better solution will be given later, but first we continue with the use of FWER.


1.3.3 Invert FWER to significant level for individual test

The identity FWER\(=1- (1-\alpha)^m\) may be inverted to find the significance level at which each individual test should be tested to attain the nominal familywise error rate at FWER, \[ \alpha = 1-(1-\text{FWER})^{1/m} \] so that the simultaneous testing procedure controls the FWER at the desired level (method of Sidàk).

Examples:

FWER\(=0.05\) and \(m=5\): \(\alpha=0.0102\)

FWER\(=0.05\) and \(m=100\): \(\alpha=0.00051\)

FWER\(=0.05\) and \(m=15443\): \(\alpha=0.0000033\).

We will argue that this procedure is too stringent for large \(m\).


To attain the familywise error rate at level FWER the individual hypotheses should be tested at very stringent significance levels when \(m\) is large. The consequence of testing at a small significance level \(\alpha\) is that it is hard to find significant results, and thus the lists of significant results (discoveries) is likely to be short. Controlling the FWER means that the chance is small that these lists contain one or more false positives. A negative consequence, however, is that many of the true positive hypothesis (i.e. \(H_1\) is true) will not appear in these short lists. Hence, the “power” is small (power is not well defined in this multiple testing setting – extensions of the concept are possible). Thus, avoiding false positives by controlling the FWER comes at a price: many of the true positive hypothesis may be missed.

1.3.4 Adjusted p-value

First we give a very general definition of an adjusted \(p\)-value.

Define the adjusted \(p\)-value as \[ \tilde{p}_i = \{\inf \alpha\in[0,1]: \text{ reject }H_{0i} \text{ at FWER } \alpha\} . \] With these adjusted \(p\)-value, the \(i\)th partial null hypothesis may be rejected when \[ \tilde{p}_i < \alpha \] while controlling the FWER at \(\alpha\).

The corrected \(p\)-value should be reported. It accounts for the multiplicity problem and it can be compared directly to the nominal FWER level to make calls at the FWER level.


2 False Discovery Rate

2.1 Introduction

The table shows the results of \(m\) hypothesis tests in a single experiment.

accept \(H_{0i}\) reject \(H_{0i}\) Total
null TN FP \(m_0\)
non-null FN TP \(m_1\)
Total NR R m
  • \(TN\): number of true negative: random and unobserved
  • \(FP\): number of false positives: random and unobserved
  • \(FN\): number of false negatives: random and unobserved
  • \(TP\): number of true positives: random and unobserved
  • \(NR\): number of acceptances (negative results): random and observed
  • \(R\): number of rejections (positive results): random and observed
  • \(m_0\) and \(m_1\): fixed and unobserved
  • \(m\): fixed and observed

Note that the table is not completely observable. It is introduced to better understand the concept of FWER and to introduce the concept of the false discovery rate (FDR).


accept \(H_{0i}\) reject \(H_{0i}\) Total
null TN FP \(m_0\)
non-null FN TP \(m_1\)
Total NR R m

The FWER can now be reexpressed as \[ \text{FWER}=\text{P}\left[\text{reject at least one }H_{0i} \mid H_0\right] = \text{P}\left[FP>0\right] . \]

The False Discovery Rate (FDR) is defined as \[ \text{FDR} = \text{E}\left[\frac{FP}{R}\right] =\text{E}\left[\text{FDP}\right] \] with \(FP/R=\text{FDP}\) the false discovery proportion and is also referred to as the false positive proportion (FPP).

Before providing more details on how to perform multiple hypothesis testing with control of the FDR, we illustrate the difference between FWER and FDR control.


The examples illustrate the problem when controlling the FWER and they demonstrate the meaning of the FDR as a more realistic risk measure.

The idea is to look at tables as on the previous slide, but from many . In its most restrictive interpretation, repeated experiments refer to replicating the same experiment many times. In the case study (brainscan) this means that each repeated experiment consists of randomly sampling 6 normal and 6 dyslectic children from a population. This is of course more like a thought experiment. The interpretation of the FWER and FDR in terms of repeated experiments, however, relates to relative frequencies and averages over tables (as on the previous slide) resulting from different independent experiments. For examples, suppose that you work for a biotech company, and each week a microarray experiment is performed (each experiment is based on e.g. 20 biological samples and aims at testing differential expression for 10,000 genes) and for each of such experiments you can think of a table as on the previous slide. The FWER and FDR may then also be interpreted as relative frequencies and averages over tables of these experiments. These experiments are not literally “repeated” experiments, but still the FWER and FDR retain their interpretation.


FWER control at \(5\%\). Suppose 100 repeated experiments have been performed. Each experiment consists of \(10,000\) hypothesis tests. Suppose that we know (this is however irrealistic) that \(1,500\) null hypotheses are not true. Below you see a few hypothetical results.

A. accept \(H_{0i}\) reject \(H_{0i}\) Tot.
true N \(8499\) \(1\) \(8500\)
true P \(1490\) \(10\) \(1500\)
Total \(9989\) \(11\) \(10000\)
B. accept \(H_{0i}\) reject \(H_{0i}\) Tot.
true N \(8497\) \(3\) \(8500\)
true P \(1463\) \(37\) \(1500\)
Total \(9960\) \(40\) \(10000\)
C. accept \(H_{0i}\) reject \(H_{0i}\) Tot.
true N \(8500\) \(0\) \(8500\)
true P \(1498\) \(2\) \(1500\)
Total \(9998\) \(2\) \(10000\)
  • When controlling the FWER at \(5\%\), results as in the tables A and B are only allowed in \(5\%\) of the experiments.
  • The other \(95\%\) of the experiments result in tables similar to C.

  • Table A gives a list of 11 discoveries, and table B gives 40 discoveries. Both lists of discoveries contain at least 1 false positive.

  • Table C gives a shorter list with only 2 discoveries which are both true positives.

  • When controlling the FWER at \(5\%\), tables with no false discoveries (table C) should make up \(95\%\) of all repeated experiments.

  • Hence, when controlling the FWER at \(5\%\) most experiments will have to result in short lists.

  • Longer lists imply a larger risk of false positives so that the FWER cannot be controlled at a level as low as \(5\%\).


FDR control at \(5\%\). Suppose 100 repeated experiments have been performed. Each experiment consists of \(10,000\) hypothesis tests. Suppose that we know (this is however irrealistic) that \(1,500\) null hypotheses are not true. Below you see a few hypothetical results.

A. accept \(H_{0i}\) reject \(H_{0i}\) Tot.
true N \(8491\) \(9\) \(8500\)
true P \(1444\) \(56\) \(1500\)
Total \(9935\) \(65\) \(10000\)
B. accept \(H_{0i}\) reject \(H_{0i}\) Tot.
true N \(8497\) \(3\) \(8500\)
true P \(1412\) \(88\) \(1500\)
Total \(9909\) \(91\) \(10000\)
C. accept \(H_{0i}\) reject \(H_{0i}\) Tot.
true N \(8500\) \(0\) \(8500\)
true P \(1498\) \(2\) \(1500\)
Total \(9998\) \(2\) \(10000\)

When controlling the FDR at \(5\%\),

  • results as in the tables A and B are very common, but
  • results as in table C are rather rare.
  • On overage \(FP/R\) is expected to be \(5\%\).

Tables A and B now give lists with 65 and 91 discoveries, respectively. Both lists contain false discoveries: \(9/65=13.8\%\) false discovery proportion for table A and \(3/91=3.3\%\) false discovery proportion for table B. Table C gives a short list with no false discoveries, i.e. \(0\%\) false discoveries proportion. When controlling the FDR at \(5\%\) each list may contain false discoveries, averaged over repeated experiments the false discoveries proportion must be equal to \(5\%\) (definition of false discovery rate). Hence, many tables as tables A and B are allowed. There is thus no need to have many tables as table C.

In summary: controlling the FDR allows for more discoveries (i.e. longer lists with significant results), while the fraction of false discoveries among the significant results in well controlled on average. As a consequence, more of the true positive hypotheses will be detected.


FWER control is too stringent:

Setting 1: BH95 with nominal FDR set at \(10\%\) and \(m_1=500\)

Consider 10 repeated experiments, with the following outcomes for \(TP\) and \(R\)

\(FP\) 11 14 8 20 12 0 11 10 13 18
\(R\) 102 110 101 159 88 91 102 140 110 171

Based on these 10 repeated experiments

  • FDR = \(\text{E}\left[FP/R\right] \approx 9.8\%\)
  • FWER = \(\text{P}\left[FP > 0 \right] \approx 90\%\)
  • sensitivity = \(\text{E}\left[TP\right]/m_1=\text{E}\left[R-FP\right]/m_1\approx 21\%\)

Setting 2: Bonferroni with nominal FWER set at \(10\%\) and \(m_1=500\)

Consider 10 repeated experiments, with the following outcomes for \(TP\) and \(R\):

\(FP\) 0 0 3 0 0 0 0 0 0 0
\(R\) 10 11 10 15 9 9 13 18 9 9

Based on these 10 repeated experiments

  • FDR = \(\text{E}\left[FP/R\right] \approx 3\%\)
  • FWER = \(\text{P}\left[FP > 0 \right] \approx 10\%\)
  • sensitivity = \(\text{E}\left[TP\right]/m_1=\text{E}\left[R-FP\right]/m_1 \approx 2\%\)

The first setting shows results from simulations in which the FDR is controlled with the BH95 method (see further) at \(10\%\) (which is good). This gives a sensitivity of \(21\%\) (not large, but realistic in large scale genomics studies). The FWER, however, is very large: 90%, but we do not mind, because we do not mind that in a large scale study we find one or more false discoveries (i.e. \(V>0\)), as long as the relative number of false discoveries is on average under control (FDR control).

The second setting shows results from simulations in which the FWER is controlled at 10%. The FDR, on the other hand, is now as small as \(3\%\) and the sensitivity as small as 2%. Hence, hardly any truly DE gene can be discovered, and, also, hardly any false discovery will end up in the conclusions of the data analysis. This analysis with FWER control is too stringent in large scale genomics context.


2.2 The Benjamini and Hochberg (1995) method

Procedure for controlling the FDR at \(\alpha\):

  1. Let \(p_{(1)}\leq \ldots \leq p_{(m)}\) denote the ordered \(p\)-values.

  2. Let \(k=\max\{i: p_{(i)}\leq i \alpha/m\}\), i.e. \(k\) is the largest integer so that \(p_{(k)}\leq k \alpha/m\).

  3. If such a \(k\) exists, reject the \(k\) null hypotheses associated with \(p_{(1)}, \ldots, p_{(k)}\). Otherwise none of the null hypotheses is rejected.

The adjusted \(p\)-value (also known as the \(q\)-value in FDR literature): \[ q_{(i)}=\tilde{p}_{(i)} = \min\left[\min_{j=i,\ldots, m}\left(m p_{(j)}/j\right), 1 \right]. \]


  • Benjamini and Hochberg published their method in 1995; it was one of the first FDR control methods.
  • The same authors published later yet other FDR control methods.
  • For this reason their 1995 method is often referred to as the Benjamini and Hochberg 1995 method, or BH95.
  • As input the method only needs the \(p\)-values from the \(m\) hypotheses tests.
  • When controlling FDR, the adjusted \(p\)-values are often referred to as \(q\)-values.

Example: \(m=5\) and FDR controlled at \(\alpha=0.05\).

\(p_{(i)}\) \(<\) or \(>\) \(i \alpha/m\) reject?
0.001 \(<\) \(1\times 0.05/5=0.01\) yes
0.007 \(<\) \(2\times 0.05/5=0.02\) yes
0.014 \(<\) \(3\times 0.05/5=0.03\) yes
0.031 \(<\) \(4\times 0.05/5=0.04\) yes
0.042 \(<\) \(5\times 0.05/5=0.05\) yes

Note that the last column can only be filled in after the largest \(k\) is detected!


Example: \(m = 7\) and FDR controlled at \(\alpha = 0.05\).

\(p_{(i)}\) \(<\) or \(>\) \(i \alpha/m\) reject?
0.001 \(<\) \(1\times 0.05/7=0.007\) yes
0.007 \(<\) \(2\times 0.05/7=0.014\) yes
0.014 \(<\) \(3\times 0.05/7=0.021\) yes
0.031 \(<\) \(4\times 0.05/7=0.029\) yes
0.035 \(<\) \(5\times 0.05/7=0.036\) yes
0.048 \(>\) \(6\times 0.05/7=0.043\) no
0.052 \(>\) \(7\times 0.05/7=0.050\) no

2.3 Intuition of BH95?

Consider \(m = 10,000\) tests

  • We will expect \(0.001 \times m_0\) tests to return false positives. A conservative estimate of the number of false positives that we can expect can be obtained by considering that the null hypotheses are true for all features, \(m_0 = m = 10000\). We then would expect \(0.001 \times 10,000 = 10\) false positives (\(FP=10\)).

  • Suppose that the researcher found 200 genes with \(p<0.001\) (\(R=200\)).

  • The proportion of false positive results (FDP = false positive proportion) among the list of \(R=200\) genes can then be estimated as \[ \widehat{\text{FDP}}=\frac{FP}{R}=\frac{10}{200}=\frac{0.001 \times 10000}{200} = 0.05. \]

Recall that the B&H (1995) procedure involves finding the largest integer \(k\) so that \(p_{(k)} \leq k \alpha /m\), or, equivalently, \(p_{(k)} m /k \leq \alpha\).

In this example: \(k=200\), \(p_{(k)}=0.001\), \(m=10,000\) and \(\alpha=0.05\).


2.3.1 Comments

  • It is a linear step-up procedure : it starts from the least significant result (largest p-value) and steps-up to more significant results (lower p-values).
  • In FDR terminology the adjusted \(p\)-value is often referred to as a \(q\)-value.
  • The BH95 method assumes that all tests are mutually independent (or at least a particular form of positive dependence between the p-values).
  • When the assumptions hold, it guarantees \[ \text{FDR}=\text{E}\left[TP/R\right]=\text{E}\left[\text{FDP}\right] \leq \frac{m_0}{m} \alpha \leq \alpha . \]

Thus, if we knew \(m_0\) (the number of true nulls), we could improve the method by applying it to the level \(\alpha m/m_0\) (cfr. Bonferroni).

\(\longrightarrow\) many FDR methods consist in estimating \(m_0\) or the fraction of null genes \(m_0/m\).


The inequality \[ \text{FDR} \leq \frac{m_0}{m} \alpha \leq \alpha \] shows that BH1995 is a conservative method, i.e. it controls the FDR at the safe side, i.e. when one is prepared to control the FDR at the nominal level \(\alpha\), the BH95 will guarantee that the true FDR is not larger than the nominal level (when the assumptions hold).

More interestingly is that \(\frac{m_0}{m} \alpha\) is in between the true FDR and the nominal FDR. Suppose that \(m_0\) were known and that the BH95 method were applied at the nominal FDR level of \(\alpha=m/m_0 \alpha^*\), in which \(\alpha^*\) is the FDR level we want to control. Then the inequality gives \[ \text{FDR} \leq \frac{m_0}{m} \alpha = \frac{m_0}{m} \frac{m}{m_0}\alpha^* = \alpha^* , \] and hence BH95 would better control the FDR at \(\alpha^*\).

Note that \(\alpha=m/m_0 \alpha^*>\alpha^*\) and hence the results is less conservative than the original BH95 method.


The above reasoning implies a generalized adaptive linear step-up procedure:

  • estimate \(m_0\): \(\hat{m}_0\)
  • of \(\hat{m}_0=0\), reject all null hypotheses; otherwise, apply the step-up procedure of BH 95 at the level \(\alpha=m \alpha^*/\hat{m}_0\) to control the FDR at \(\alpha^*\).

The adjusted \(p\)-values (=\(q\)-values) are obtained as \[ \tilde{p}_{(i)} = \frac{\hat{m}_0}{m} \min\left\{\min_{j=i,\ldots, m}\{m p_{(j)}/j\} ,1 \right\}. \]

  • Many FDR procedures can be fit into this definition (e.g. Benjamini and Hochberg (2000) and Tibshirani (2003)).
  • We do not give details on the methods for estimating \(m_0\), but some of them are implemented in the R software. On the next page we illustrate with simulated data that BH can be improved with estimated \(m_0\).

2.3.2 Other important considerations

  • It can be shown that the BH-FDR method weakly controls the FWER, i.e. it controls the FWER if all features are false (\(m_0=m\)).

  • The BH-FDR is derived under the assumption of independence of the features and has been shown to be only valid under special forms of dependence between the features.


2.4 Brain Example

dti %>%
  ggplot(aes(x = p.value)) +
  geom_histogram(color = "black",breaks = seq(0,1,.05))

  • The graph shows the histogram of the \(m=15443\) \(p\)-values. It shows a distribution which is close to a uniform distribution for the larger p-values, but with more small \(p\)-values than expected under a uniform distribution.

  • This is a trend that would arise if most of the hypotheses are nulls (resulting in \(p\)-values from a uniform distribution), but some are non-nulls (more likely to result in small \(p\)-values).


dti <- dti %>%
  mutate(
    padj = p.adjust(p.value, method="fdr"),
    zFDR = (padj < 0.05) * z.value)

pPadj <- dti %>%
  ggplot(aes(p.value,padj)) +
  geom_point() +
  geom_segment(x=0,y=0,xend=1,yend=1) +
  ylab("adjusted p-value (BH, 1995)")

grid.arrange(pPadj,
  pPadj + ylim(c(0,0.05)),
  ncol=2)
## Warning: Removed 15411 rows containing missing values (geom_point).

# uncorrected p-values
table(dti$p.value < 0.05)
## 
## FALSE  TRUE 
## 14202  1241
# BH corrected p-values
table(dti$padj < 0.05)
## 
## FALSE  TRUE 
## 15411    32

At the 5% FDR, 32 voxels are returned as significantly differentially active between dyslexic and non-dyslexic children.

pFDR <- dti %>%
  ggplot(
    aes(
      coord.y,
      coord.x,
      color=zFDR)
    ) +
  geom_point() +
  scale_colour_gradient2(low = "blue",mid="white",high="red") +
  transition_manual(coord.z) +
  labs(title = "transection z = {frame}") +
  theme_grey()

3 local fdr

3.1 Introduction

Suppose that the test statistic for testing \(H_{0i}\) is denoted by \(z_i\), and that the test statistics have a \(N(0,1)\) null distribution.

If all \(m\) null hypotheses are true, the histogram of the \(m\) test statistics should approximate the theoretical null distribution (density \(f_0(z)\)).

## `stat_bin()` using `bins = 30`. Pick better value with `binwidth`.

Assuming that the test statistic has a standard normal null distribution is not restrictive. For example, suppose that \(t\)-tests have been applied and that the null distribution is \(t_d\), with \(d\) representing the degrees of freedom. Let \(F_{td}\) denote the distribution function of \(t_d\) and let \(\Phi\) denote the distribution function of the standard normal distribution. If \(T\) denotes the \(t\)-test statistic, then, under the null hypothesis, \[ T \sim t_d \] and hence \[ F_{td}(T) \sim U[0,1] \] and \[ Z = \Phi^{-1}(F_{td}(T)) \sim N(0,1). \] If all \(m\) null hypotheses are true, then each of the \(Z_i\) is \(N(0,1)\) and the set of \(m\) calculated \(z_i\) test statistics may be considered as a sample from \(N(0,1)\). Hence, under these conditions we expect the histogram of the \(m\) \(z_i\)’s to look like the density of the null distribution.

3.2 Two group model

  • Suppose that under the alternative hypothesis the test statistic has density function \(f_1(z)\).

  • We use the term “null” to refer to a case \(i\) for which \(H_{0i}\) is true, and “non-null” for a case \(i\) for which \(H_{0i}\) is not true.

  • Consider the prior probabilities \[ \pi_0 = \text{P}\left[\text{null}\right] \text{ and } \pi_1=\text{P}\left[\text{non-null}\right] = 1-\pi_0. \]

  • The marginal distribution of the \(m\) test statistics is then given by the mixture distribution

\[ f(z) = \pi_0 f_0(z) + \pi_1 f_1(z) \]

3.2.1 Examples of mixture distributions

We have already explored mixture distributions in detail in the paper reading session on model based clustering.

  • blue: \(f_0\): \(N(0,1)\), red: \(f_1\): \(N(1,1)\)
components <- tibble(z = seq(-6,6,.01)) %>%
  mutate(
    f0 = dnorm(z),
    f1 = dnorm(z, mean = 1))

components %>%
  gather(component, density, -z) %>%
  ggplot(aes(z,density,color = component)) +
  geom_line() +
  scale_color_manual(values=c("blue","red"))

The graphs shows the two component distributions separately.


  • blue: \(\pi_0 \times f_0\) with \(\pi_0=0.9\) and \(f_0 = N(0,1)\)
  • red: \(\pi_1\times f_1\) with \(\pi_1=1-\pi_0=0.1\) and \(f_1 = N(1,1)\)
p0 <- 0.9
p1 <- 1-p0
mu1 <- 1
scaledComponents <- tibble(z = seq(-6,6,.01)) %>%
  mutate(
    p0xf0 = dnorm(z) * p0,
    p1xf1 = dnorm(z, mean = mu1)*p1
    )

scaledComponents %>%
  gather(component, density, -z) %>%
  ggplot(aes(z,density,color = component)) +
  geom_line() +
  scale_color_manual(values=c("blue","red")) +
  ggtitle("Scaled components")


Mixture distribution

  • blue: \(\pi_0 \times f_0\) with \(\pi_0=0.9\) and \(f_0 = N(0,1)\)
  • red: \(\pi_1\times f_1\) with \(\pi_1=1-\pi_0=0.1\) and \(f_1 = N(1,1)\)
  • black: \(f=\pi_0 f_0 + \pi_1 f_1\)
scaledComponents %>%
  mutate(f=p0xf0+p1xf1) %>%
  gather(component, density, -z) %>%
  ggplot(aes(z,density,color = component)) +
  geom_line() +
  scale_color_manual(values=c("black","blue","red")) +
  ggtitle("Mixture and scaled components")


Mixture \(\pi_0 f_0(z)+\pi_1 f_1(z)\) with \(\pi_0=0.65\) and \(f_1= N(2,1)\) and \(f_0 = N(0,1)\)

p0 <- 0.65
p1 <- 1-p0
mu1 <- 2
scaledComponents <- tibble(z = seq(-6,6,.01)) %>%
  mutate(
    p0xf0 = dnorm(z) * p0,
    p1xf1 = dnorm(z, mean = mu1)*p1)

scaledComponents %>%
  mutate(f=p0xf0+p1xf1) %>%
  gather(component, density, -z) %>%
  ggplot(aes(z,density,color = component)) +
  geom_line() +
  scale_color_manual(values=c("black","blue","red")) +
  ggtitle("Mixture and scaled components (p0 = 0.35)")

3.2.2 simulations

Simulated data: 20000 \(z\)-statistics with \(\pi_1=0.10\) non-nulls with \(f_1=N(1,1)\).

p0 <- .9
p1 <- 1-p0
mu1 <- 1
m <- 20000

zSim <- c(
  rnorm(m * p0),
  rnorm(m * p1, mean=mu1)
  )

zSim %>%
  as_tibble %>%
  ggplot(aes(x = zSim)) +
  geom_histogram(
    aes(y=..density..),
    color = "black") +
  stat_function(fun = dnorm,
    args = list(
      mean = 0,
      sd=1),
    color="blue")
## `stat_bin()` using `bins = 30`. Pick better value with `binwidth`.

It is hard to see the difference between the histogram and the density function of the null distribution (blue curve), because the mean of \(f_1\) is not much larger than 0 and because only \(\pi_1=10\%\) non-nulls are included and because the alternative is not far from the null distribution. However, this is not an unrealistic setting.

Note, that in most settings the non-null features will originate from a mixture of multiple distributions with positive and negative means. Fortunately, the local fdr method does not require us to estimate \(f_1\) as we will see further.


3.3 local fdr

We can now calculate the probability that a case is a null given the observed \(z\), \[ \text{P}\left[\text{null}\mid z\right] = \frac{\pi_0 f_0(z)}{f(z)} . \] This probability is referred to as the local false discovery rate, and denoted by fdr\((z)\).

If for an observed \(z\), fdr\((z)\) is sufficiently small, one may believe that the case is a true discovery (i.e. \(H_{0i}\) may be rejected).

3.3.2 Estimation of fdr\((z)=\frac{\pi_0 f_0(z)}{f(z)}\)

  • \(f(z)\) can be estimated by nonparametric density estimation methods (\(f(z)\) is the marginal distribution of the test statistics; no knowledge about null / non-null is needed)

  • \(f_0(z)\) is known or can be estimated from the data

  • \(\pi_0\) can be estimated once \(f(z)\) and \(f_0(z)\) are estimated for all \(z\).


3.3.3 Brainscan example

library(locfdr)
lfdr <- locfdr(dti$z.value, nulltype = 0)

  • In the brainscan example the test statistics are supposed to be \(N(0,1)\) distributed under the null hypothesis. Tests are performed two-sided.

  • The argument nulltype=0 specifies that the null distribution (\(f_0\)) is \(N(0,1)\).

  • The dashed blue line gives \(f_0\) and the solid green line is the nonparametric estimate of the marginal density function \(f\). The two densities do not coincide and hence we may anticipate that some of the voxels show differential brain activity.

  • The purple bars indicate the estimated number of non-nulls (among the hypotheses/voxels for a given \(z\)-value). The plots shows that more non-nulls are expected for the negative \(z\)-values than for the positive \(z\)-values (sign of \(z\) corresponds to more or less brain activity in normal versus dyslectic children).

3.3.4 Problems?

Note, however, that

  • we typically expect that the majority of the test statistics follow the null distribution.
  • that the null distribution in the plot is rescaled
  • So, we would expect that the two distributions to overlay in the middle part.
  • However, we observe a shift.

In practise it often happens that the theoretical null distribution is not valid.

This can happen due to

  1. Failed mathematical assumptions: null distribution is incorrect
  2. Correlation between the samples
  3. Correlation between the features
  4. Confounding that is not corrected for.

3.4 Advantage of having a massive parallel data structure

The massive parallel data structure enables us

  • to spot deviations from the theoretical null distribution.
  • to estimate the null distribution by using all features.

Efron relaxes the local fdr method by assuming that the null distribution is a Normal distribution but with a mean and variance that can be estimated empirically (based on all the features).

This can be done by setting the argument nulltype in the locfdr function equal to nulltype = 1, which is the default or be setting nulltype = 2.

The locfdr method then uses

  1. nulltype = 1 maximum likelihood to estimate the null by only considering the middle part in the distribution of the test statistics (MLE) or
  2. nulltype = 2 a geometric method that places the best fitting normal under the peak of the estimate of f(z). (CME)

3.4.1 Brainscan example

lfdr <- locfdr(dti$z.value)

The plot shows that the null distribution is shifted to negative values and has a standard deviation that remains close to 1.

  • This often happens if there is correlation between the features.

  • Spatial correlation can be expected in the brain, so voxels that are close to each-other typically will be correlated.

  • The dashed blue line gives \(f_0\) and the solid green line is the nonparametric estimate of the marginal density function \(f\). The two densities do not coincide and hence we may anticipate that some of the voxels show differential brain activity.

  • The purple bars indicate the estimated number of non-nulls (among the hypotheses/voxels for a given \(z\)-value). The plots shows that only non-nulls for positive \(z\)-values are expected (sign of \(z\) corresponds to more or less brain activity in normal versus dyslectic children).


lfdr <- locfdr(dti$z.value, plot=2)

  • The plot at the left is the same as on the previous page.

  • The plot at the right shows the local fdr as the black solid line. Close to \(z=0\) the fdr is about 1 (i.e. if those hypotheses would be rejected, the probability of a false positive is about \(100\%\)). When moving away from \(z=0\) to larger values the fdr drops.

  • This means that we can only discover convincingly differential brain activity for large positive \(z\). Rejecting null hypotheses with large negative \(z\) would still be risky: large chance of false discovery.

  • The reason can be read from the first graph: for negative \(z\) the ratio \(f_0(z)/f(z)\) is almost 1, whereas for large positive \(z\) the ratio \(f_0(z)/f(z)\) becomes small.

  • Note, that the result is atypically. In most applications we typically pick-up both downregulated (negative z) and upregulated (positive z) features.


dti <- dti %>%
  mutate(
    lfdr = lfdr$fdr,
    zfdr = (lfdr<0.2) * z.value)

pfdr <- dti %>%
  ggplot(
    aes(
      coord.y,
      coord.x,
      color=zfdr)
    ) +
  geom_point() +
  scale_colour_gradient2(low = "blue",mid="white",high="red") +
  transition_manual(coord.z) +
  labs(title = "transection z = {frame}") +
  theme_grey()

Note, that the local fdr method allows us to detect differential brain activity in a specific region in the front part of the brain for which a larger fractional anisotropy is observed on average for childeren having dyslexia.

We can also estimate the FDR of the set that we return as the average local fdr in this set.

dti %>%
  filter(lfdr < 0.2) %>%
  pull(lfdr) %>%
  mean
## [1] 0.1034925

3.5 Power

The local false discovery rate may also be used to get power diagnostics.

General idea: for \(z\)’s supported by the alternative hypothesis (i.e. large \(f_1(z)\)), we hope to see small fdr\((z)\).

The expected fdr is an appropriate summary measure: \[ \text{Efdr} = \text{E}_{f1}\left[\text{fdr}(Z)\right] = \int_{-\infty}^{+\infty} \text{fdr}(z) f_1(z) dz. \]

With estimates of fdr\((z)\) and \(f_1(z)\), the Efdr can be computed.

A small Efdr is an indication of a powerful study.

lfdr <- locfdr(dti$z.value, plot = 3)

With \(\alpha\) the nominal local fdr level, the vertical axis gives \[ \text{E}_{f_1}\left[\text{fdr}(Z)<\alpha\right]. \]

where \(Z\) is the test statistic distributed under the alternative hypothesis (\(f_1\)).

  • This probability \(\text{P}_{f_1}\left[\text{fdr}(Z)<\alpha\right]\) is a kind of extension of the definition of the power of a test: it is the probability that a non-null can be detected when the nominal local fdr is set at \(\alpha\).

  • The graph shows, for examples, that with \(\alpha=0.20\) we only have \(\text{P}_{f_1}\left[\text{fdr}(Z)<\alpha\right] =0.24\), i.e. only \(24\%\) of the non-nulls are expected to be discovered.

  • At the bottom of the graph we read Efdr\(=0.486\). Hence, the local fdr for a typical non-null feature is expected to be 48.6% which is rather large. The study is not well powered!

LS0tCnRpdGxlOiAiTGFyZ2UgU2NhbGUgSW5mZXJlbmNlIgphdXRob3I6ICJMaWV2ZW4gQ2xlbWVudCIKZGF0ZTogInN0YXRPbWljcywgR2hlbnQgVW5pdmVyc2l0eSAoaHR0cHM6Ly9zdGF0b21pY3MuZ2l0aHViLmlvKSIKb3V0cHV0OgogICAgaHRtbF9kb2N1bWVudDoKICAgICAgY29kZV9kb3dubG9hZDogdHJ1ZQogICAgICB0aGVtZTogY29zbW8KICAgICAgdG9jOiB0cnVlCiAgICAgIHRvY19mbG9hdDogdHJ1ZQogICAgICBoaWdobGlnaHQ6IHRhbmdvCiAgICAgIG51bWJlcl9zZWN0aW9uczogdHJ1ZQogICAgcGRmX2RvY3VtZW50OgogICAgICB0b2M6IHRydWUKICAgICAgbnVtYmVyX3NlY3Rpb25zOiB0cnVlCi0tLQoKIyBNb3RpdmF0aW9uCgojIyBCcmFpbiBpbWFnaW5nIHN0dWR5CgoKYGBge3IgZmlnLmFsaWduPSJjZW50ZXIiLCBvdXQud2lkdGggPSAnODAlJywgZWNobz1GQUxTRX0Ka25pdHI6OmluY2x1ZGVfZ3JhcGhpY3MoIi4vZmlndXJlcy9EVElDb2xvci5qcGciKQpgYGAKCi0gRGlmZnVzaW9uIFRlbnNvciBJbWFnaW5nIChEVEkpIGRhdGEKLSBEVEkgbWVhc3VyZXMgZmx1aWQgZmxvd3MgaW4gdGhlIGJyYWluCi0gQ29tcGFyaW5nIGJyYWluIGFjdGl2aXR5IG9mIHNpeCBkeXNsZXhpYyBjaGlsZHJlbiB2ZXJzdXMgc2l4IG5vcm1hbCBjb250cm9scwotIEZyb20gZWFjaCBjaGlsZCwgRFRJIHByb2R1Y2VkIG9ic2VydmF0aW9ucyBvbiAxNTQ0MyB2b3hlbHMgKHZveGVsID0gc21hbGwgdm9sdW1lIGF0IGEgcGFydGljdWxhciAoeCwgeSwgeCkgY29vcmRpbmF0ZSkKLSBGb3IgZWFjaCB2b3hlbCwgYSB0d28tc2lkZWQgdHdvLXNhbXBsZSB0LXRlc3QgaGFzIGJlZW4gcGVyZm9ybWVkLCByZXN1bHRpbmcgaW4gYSB6LXZhbHVlICgxNTQ0MyB6LXZhbHVlcykgZm9yIGZyYWN0aW9uYWwgYW5pc290cm9weS4KLSAgTG93IHZhbHVlcyBmb3IgRkEgaW5kaWNhdGUgZGlmZnVzaW9uIGluIGFsbCBkaXJlY3Rpb25zLCBoaWdoIHZhbHVlcyBpbmRpY2F0ZXMgZGlyZWN0aW9uYWwgZGlmZnVzaW9uLgotIFJlc2VhcmNoIHF1ZXN0aW9uOiBhdCB3aGF0IGJyYWluIGxvY2F0aW9ucyAodm94ZWxzKSBzaG93IGR5c2xleGljIGNoaWxkcmVuIGEgZGlmZmVyZW50IGJyYWluIGFjdGl2aXR5IGFzIGNvbXBhcmVkIHRvIGNoaWxkcmVuIHdpdGhvdXQgZHlzbGV4aWE/CgpGb3IgZWFjaCB2b3hlbCBzZXBhcmF0ZWx5LCB0aGlzIGlzIGEgc2ltcGxlIHByb2JsZW0sIGJ1dCB0aGUgbGFyZ2Ugc2NhbGUgb2YgdGhlIHByb2JsZW0gKDE1NDQzIHNpbXVsdGFuZW91cyBoeXBvdGhlc2lzIHRlc3RzKSBjYXVzZXMgdGhlIHByb2JsZW0gb2YgbXVsdGlwbGljaXR5LgoKIyMjIERhdGEgRXhwbG9yYXRpb24KClRoZSBkYXRhc2V0IGBkdGlgIGNvbnRhaW5zCgotIFNwYXRpYWwgbG9jYXRpb24gKHgsIHksIHopIG9mIGVhY2ggdm94ZWwKLSB6LXN0YXRpc3RpYyBmb3IgYXNzZXNzaW5nIGRpZmZlcmVudGlhbCBicmFpbiBhY3Rpdml0eSBiZXR3ZWVuIGR5c2xleGljIGFuZCBub24tZHlzbGV4aWMgY2hpbGRyZW4KCgoKYGBge3IsIG1lc3NhZ2U9RkFMU0V9CmxpYnJhcnkodGlkeXZlcnNlKQpsaWJyYXJ5KGxvY2ZkcikKbGlicmFyeShnZ2FuaW1hdGUpCgpkdGkgPC0gcmVhZF9jc3YoImh0dHBzOi8vcmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbS9zdGF0T21pY3MvSERBMjAyMC9kYXRhL2R0aS5jc3YiLAogICAgICAgICAgICAgICAgY29sX3R5cGVzID0gY29scygpKQpgYGAKCmBgYHtyfQpwWiA8LSBkdGkgJT4lCiAgZ2dwbG90KAogICAgYWVzKAogICAgICBjb29yZC55LAogICAgICBjb29yZC54LAogICAgICBjb2xvcj16LnZhbHVlKQogICAgKSArCiAgZ2VvbV9wb2ludCgpICsKICBzY2FsZV9jb2xvdXJfZ3JhZGllbnQyKGxvdyA9ICJibHVlIixtaWQ9IndoaXRlIixoaWdoPSJyZWQiKSArCiAgdHJhbnNpdGlvbl9tYW51YWwoY29vcmQueikgKwogIGxhYnModGl0bGUgPSAidHJhbnNlY3Rpb24geiA9IHtmcmFtZX0iKSArCiAgdGhlbWVfZ3JleSgpCmBgYAoKV2Ugd2lsbCBub3cgcGxvdCB0aGUgYW5pbWF0ZWQgZ3JhcGgKCmBgYHtyIGV2YWw9RkFMU0V9CnBaCmBgYAoKCl9fV0FSTklOR19fOiBUaGUgYW5pbWF0ZWQgZ3JhcGggd2lsbCBvbmx5IGJlIHZpc2libGUgaW4gdGhlIEhUTUwgb3V0cHV0LCBub3QgaW4gUERGIGZvcm1hdC4KSWYgeW91J3JlIHJlYWRpbmcgdGhlIFBERiB2ZXJzaW9uLCBjaGVjayBbb25saW5lXShodHRwczovL3N0YXRvbWljcy5naXRodWIuaW8vSERBMjAyMC9sc2kuaHRtbCMxMTFfRGF0YV9FeHBsb3JhdGlvbikKZm9yIHRoZSBhbmltYXRlZCBncmFwaC4KCmBgYHtyIGVjaG89RkFMU0UsIG1lc3NhZ2U9RkFMU0UsIGV2YWw9a25pdHI6OmlzX2h0bWxfb3V0cHV0KCl9CmFuaW1hdGUocFosIG5mcmFtZXMgPSAxMDMsIGVuZF9wYXVzZSA9IDMpCmBgYAoKV2UgdmlzdWFsaXNlZCB0aGUgdGVzdC1zdGF0aXN0aWMgb2YgZWFjaCB0ZXN0IHBlciB2b3hlbCEKCk5vdGUsIHRoYXQgaXQgaXMgZGlmZmljdWx0IHRvIHNlZSBzdHJ1Y3R1cmUgaW4gdGhlIGRhdGEuCgojIyMgSW5mZXJlbmNlCgpXZSBjYW4gY29udmVydCB0aGUgei1zdGF0aXN0aWMgaW4gYSB0d28tc2lkZWQgcC12YWx1ZSBmb3IgZWFjaCB2b3hlbCB0byBhc3Nlc3MKClxbSF8wOiBcdGV4dHtUaGVyZSBpcyBvbiBhdmVyYWdlIG5vIGRpZmZlcmVuY2UgaW4gYnJhaW4gYWN0aXZpdHkgaW4gdm94ZWwgeHl6IGJldHdlZW4gZHlzbGV4aWMgYW5kIG5vbi1keXNsZXhpYyBjaGlsZHJlbn1cXQogIFxbXG11X2Q9XG11X3tuZH1cXQoKdnMKClxbSF8wOiBcdGV4dHtUaGVyZSBpcyBvbiBhdmVyYWdlIGEgZGlmZmVyZW5jZSBpbiBicmFpbiBhY3Rpdml0eSBpbiB2b3hlbCB4eXogYmV0d2VlbiBkeXNsZXhpYyBhbmQgbm9uLWR5c2xleGljIGNoaWxkcmVufVxdCiAgXFtcbXVfZFxuZXFcbXVfe25kfVxdCgoKQmVsb3csIHdlIGNhbGN1bGF0ZSB0aGUgcC12YWx1ZXMgYW5kIGEgdmFyaWFibGUgelAgZm9yIHdoaWNoIHdlIGtlZXAgdGhlIHotdmFsdWUgaWYgaXQgaXMgc3RhdGlzdGljYWwgc2lnbmlmaWNhbnQgYXQgdGhlIDUlIGxldmVsIG90aGVyd2lzZSB3ZSBzZXQgaXQgZXF1YWwgdG8gelA9MC4KCmBgYHtyfQpkdGkgPC0gZHRpICU+JQogIG11dGF0ZSgKICAgIHAudmFsdWUgPSBwbm9ybShhYnMoei52YWx1ZSksbG93ZXI9RkFMU0UpKjIsCiAgICB6UCA9IChwLnZhbHVlIDwgMC4wNSkgKiB6LnZhbHVlKQoKcFB2YWwgPC0gZHRpICU+JQogIGdncGxvdCgKICAgIGFlcygKICAgICAgY29vcmQueSwKICAgICAgY29vcmQueCwKICAgICAgY29sb3I9elApCiAgICApICsKICBnZW9tX3BvaW50KCkgKwogIHNjYWxlX2NvbG91cl9ncmFkaWVudDIobG93ID0gImJsdWUiLG1pZD0id2hpdGUiLGhpZ2g9InJlZCIpICsKICB0cmFuc2l0aW9uX21hbnVhbChjb29yZC56KSArCiAgbGFicyh0aXRsZSA9ICJ0cmFuc2VjdGlvbiB6ID0ge2ZyYW1lfSIpICsKICB0aGVtZV9ncmV5KCkKYGBgCgoKV2Ugd2lsbCBub3cgcGxvdCB0aGUgYW5pbWF0ZWQgZ3JhcGgKCmBgYHtyIGV2YWw9RkFMU0V9CnBQdmFsCmBgYAoKYGBge3IgZWNobz1GQUxTRSwgbWVzc2FnZT1GQUxTRSwgZXZhbD1rbml0cjo6aXNfaHRtbF9vdXRwdXQoKX0KYW5pbWF0ZShwUHZhbCwgbmZyYW1lcyA9IDEwMywgZW5kX3BhdXNlID0gMykKYGBgCgpJdCBpcyBtdWNoIG1vcmUgZWFzeSB0byBvYnNlcnZlIHBhdHRlcm5zIG9mIGFjdGl2aXR5LgoKTm90ZSwgaG93ZXZlciB0aGF0CgotIEhpZ2hlciBhdmVyYWdlIEZBICh6ID4gMCBhbmQgcCA8IDAuMDUpIGluIGR5c2xleGljIGNoaWxkcmVuIGlzIGFwcGVhcmluZyBpbiBzcGF0aWFsIHBhdHRlcm5zIGluIHNvbWUgbG9jYXRpb25zLgotIExvd2VyIGF2ZXJhZ2UgRkEgKHogPCAwIGFuZCBwID4gMC4wNSkgaW4gZHlzbGV4aWMgY2hpbGRyZW4gaXMgc2NhdHRlcmVkIHRocm91Z2hvdXQgdGhlIGJyYWluLgotIE11bHRpcGxlIHRlc3RpbmcgcHJvYmxlbS4KLSBJZiB0aGVyZSB3b3VsZCBiZSBubyBhc3NvY2lhdGlvbiBiZXR3ZWVuIGJyYWluIGFjdGl2aXR5IGFuZCBkeXNsZXhpYSB3ZSBjYW4gZXhwZWN0IG9uIGF2ZXJhZ2UgYHIgbnJvdyhkdGkpICogMC4wNWAgZmFsc2UgcG9zaXRpdmUgdm94ZWxzLgotIE5vdGUsIHRoYXQgb25seSBgciBzdW0oZHRpJHAudmFsdWUgPCAwLjA1KWAgd2VyZSBzaWduaWZpY2FudCBhdCB0aGUgNSUgc2lnbmlmaWNhbmNlIGxldmVsLCBzbyB3ZSBjYW4gZXhwZWN0IHRoYXQgdGhlIG1ham9yaXR5IG9mIHRoZSByZXR1cm5lZCB2b3hlbHMgYXJlIGZhbHNlIHBvc2l0aXZlcy4KCmBgYHtyfQpGUGV4cGVjdGVkICA8LSBucm93KGR0aSkgKiAwLjA1ClByZXBvcnRlZCA8LSBzdW0oZHRpJHAudmFsdWUgPCAwLjA1KQoKRlBleHBlY3RlZApQcmVwb3J0ZWQKYGBgCgojIyBDaGFsbGVuZ2VzCgpMYXJnZSBTY2FsZSBJbmZlcmVuY2UgaW1wbGllcwoKLSBNYW55IGh5cG90aGVzaXMgdG8gYmUgZXZhbHVhdGVkCi0gSHVnZSBtdWx0aXBsZSB0ZXN0aW5nIHByb2JsZW0KLSBNYW55IGZhbHNlIHBvc2l0aXZlcyBjYW4gYmUgZXhwZWN0ZWQgaWYgd2UgZG8gbm90IGNvcnJlY3QgZm9yIG11bHRpcGxlIHRlc3RpbmcKCklzc3VlIGlzIHdpZGVzcHJlYWQgaW4gbWFueSBkaXNjaXBsaW5lcwoKLSBnZW5vbWljcwotIHRyYW5zY3JpcHRvbWljcwotIHByb3Rlb21pY3MKLSBicmFpbiBpbWFnaW5nCi0gaGlnaCB0aHJvdWdocHV0IHNpbmdsZSBjZWxsIHRlY2hub2xvZ2llcwotIGRldGVjdGlvbiBvZiBhbm9tYWxvdXMgZXZlbnRzOiBlLmcuIGNyZWRpdCBjYXJkIGZyYXVkCi0gZXZhbHVhdGlvbiBvZiB0cmFkaW5nIHJ1bGVzCi0gYWNhZGVtaWMgcGVyZm9ybWFuY2Ugb2Ygc2Nob29scwoKIyMgTXVsdGlwbGljaXR5IFByb2JsZW0KClN1cHBvc2Ugb25seSBhIHNpbmdsZSBoeXBvdGhlc2lzIHRlc3QgaXMgcmVxdWlyZWQgZm9yIGFuc3dlcmluZyB0aGUgcmVzZWFyY2ggcXVlc3Rpb24uIEEgc3RhdGlzdGljYWwgdGVzdCBjb250cm9scyB0aGUgcHJvYmFiaWxpdHkgb2YgbWFraW5nIGEgKip0eXBlIEkgZXJyb3IqKiAodHlwZSBJIGVycm9yIHJhdGUpLApcWwogICBcYWxwaGEgPVx0ZXh0e1B9XGxlZnRbXHRleHR7cmVqZWN0IH1IXzAgXG1pZCBIXzBccmlnaHRdIC4KXF0KVGhlIHR5cGUgSSBlcnJvciBpcyBhbHNvIGtub3duIGFzIGEgKipmYWxzZSBwb3NpdGl2ZSoqIChpLmUuICRIXzAkIGV4cHJlc3NlcyBhbiBuZWdhdGl2ZSByZXN1bHQsIGFuZCAkSF8xJCBhIHBvc2l0aXZlIHJlc3VsdCk6ICRcYWxwaGE9XHRleHR7UH1cbGVmdFtcdGV4dHtmYWxzZSBwb3NpdGl2ZX1ccmlnaHRdJC4KCkFuIGltcG9ydGFudCBwcm9wZXJ0eToKCldoZW4gJEhfMCQgaXMgdHJ1ZSwgYW5kIHRoZSBhc3N1bXB0aW9ucyB1bmRlcmx5aW5nIHRoZSB0ZXN0IGhvbGQgdHJ1ZSwgdGhlbgpcWwogIFAgXHNpbSBVWzAsMV0gLgpcXQpIZW5jZSwgZm9yIGFueSAkMDxcYWxwaGE8MSQsClxbCiAgXHRleHR7UH1cbGVmdFtcdGV4dHtyZWplY3QgfUhfMCBcbWlkIEhfMFxyaWdodF0gPSBcdGV4dHtQfVxsZWZ0W1A8XGFscGhhIFxtaWQgSF8wXHJpZ2h0XSA9IFxhbHBoYS4KXF0KClRoZSBkaXN0cmlidXRpb24gb2YgdGhlIHotc3RhdGlzdGljIGFuZCB0aGUgcC12YWx1ZXMgdW5kZXIgJEhfMCQgYXJlIGlsbHVzdHJhdGVkIGJlbG93OgoKYGBge3J9CmxpYnJhcnkoZ3JpZEV4dHJhKQoKc2ltRGF0YSA8LSB0aWJibGUoCiAgei52YWx1ZSA9IHJub3JtKDIwMDAwKQogICkKCnNpbURhdGEgPC0gc2ltRGF0YSAlPiUgbXV0YXRlKHAudmFsdWUgPSAyKigxLXBub3JtKGFicyh6LnZhbHVlKSkpKQoKcDEgPC0gc2ltRGF0YSAlPiUKICBnZ3Bsb3QoYWVzKHggPSB6LnZhbHVlKSkgKwogIGdlb21faGlzdG9ncmFtKAogICAgYWVzKHk9Li5kZW5zaXR5Li4pLAogICAgY29sb3IgPSAiYmxhY2siKSArCiAgc3RhdF9mdW5jdGlvbihmdW4gPSBkbm9ybSwgYXJncz1saXN0KG1lYW49MCwgc2Q9MSkpCgpwMiA8LSBzaW1EYXRhICU+JQogIGdncGxvdChhZXMoeCA9IHAudmFsdWUpKSArCiAgZ2VvbV9oaXN0b2dyYW0oY29sb3IgPSAiYmxhY2siLCBicmVha3MgPSBzZXEoMCwxLC4wNSkpCgpncmlkLmFycmFuZ2UocDEsIHAyLCBuY29sPTIpCmBgYAoKV2UgaW5kZWVkIG9ic2VydmUgdGhhdCB0aGUgcC12YWx1ZXMgYXJlIHVuaWZvcm0gdW5kZXIgdGhlIG51bGwgaHlwb3RoZXNpcy4gU28gc3RhdGlzdGljYWwgaHlwb3RoZXNpcyB0ZXN0aW5nIHByb3ZpZGVzIGEgdW5pZm9ybSB0ZXN0aW5nIHN0cmF0ZWd5LgoKIyMjIE5vdGF0aW9uCgpJbiB0aGUgbXVsdGlwbGUgdGVzdGluZyBsaXRlcmF0dXJlIHRoZSBudW1iZXIgb2YgZmVhdHVyZXMgdGhhdCBmb3Igd2hpY2ggYSB0ZXN0IGlzIGNvbmR1Y3RlZCBpcyBkZW5vdGVkIGJ5ICRtJCBpbnN0ZWFkIG9mICRwJCB0byBhdm9pZCBjb25mdXNpb24gd2l0aCB0aGUgc3ltYm9sIGZvciBhIHAtdmFsdWUuCgojIyMgIEZhbWlseXdpc2UgZXJyb3IgcmF0ZQoKU3VwcG9zZSB0aGF0ICRtJCBoeXBvdGhlc2VzIGhhdmUgdG8gYmUgdGVzdGVkIHNpbXVsdGFuZW91c2x5IGZvciBhbnN3ZXJpbmcgYSBzaW5nbGUgcmVzZWFyY2ggcXVlc3Rpb24uCgpMZXQgJEhfezBpfSQgZGVub3RlIHRoZSAkaSR0aCBudWxsIGh5cG90aGVzaXMgKCRpPTEsXGxkb3RzLCBtJCkgYW5kIGxldCAkSF8wJCBkZW5vdGUgdGhlIGludGVyc2VjdGlvbiBvZiBhbGwgdGhlc2UgcGFydGlhbCBudWxsIGh5cG90aGVzZXMuCgogSW4gdGhpcyBjYXNlIHRoZSB0eXBlIEkgZXJyb3IgcmF0ZSBpcyBubyBsb25nZXIgcmVsZXZhbnQuIEluc3RlYWQgb25lIG1heSBjb25zaWRlciB0aGUgKipGYW1pbHl3aXNlIEVycm9yIFJhdGUgKEZXRVIpKioKIFxbCiAgIFx0ZXh0e0ZXRVJ9PVx0ZXh0e1B9XGxlZnRbXHRleHR7cmVqZWN0IGF0IGxlYXN0IG9uZSB9SF97MGl9IFxtaWQgSF8wXHJpZ2h0XS4KIFxdCgoKQXNzdW1pbmcgaW5kZXBlbmRlbmNlIGFtb25nIHRoZSAkbSQgdGVzdHMgYW5kIGFzc3VtaW5nIHRoYXQgYWxsIGluZGl2aWR1YWwgdGVzdHMgYXJlIHBlcmZvcm1lZCBhdCB0aGUgJFxhbHBoYSQgbGV2ZWwgb2Ygc2lnbmlmaWNhbmNlLCB0aGUgRldFUiBjYW4gYmUgY29tcHV0ZWQgYXMKClxbClxiZWdpbnthcnJheX17cmNsfQpcdGV4dHtGV0VSfQomPSYgXHRleHR7UH1cbGVmdFtcdGV4dHtyZWplY3QgYXQgbGVhc3Qgb25lIH1IX3swaX0gXG1pZCBIXzBccmlnaHRdIFxcCiY9JiAxIC0gXHRleHR7UH1cbGVmdFtcdGV4dHtyZWplY3Qgbm8gfUhfezBpfSBcbWlkIEhfMFxyaWdodF0gXFwKJj0mIDEtIFx0ZXh0e1B9XGxlZnRbXHRleHR7bm90IHJlamVjdCB9SF97MDF9XHRleHR7IGFuZCB9XGxkb3RzXHRleHR7IGFuZCBub3QgcmVqZWN0IH1IX3swbX0gXG1pZCBIXzBccmlnaHRdIFxcCiY9JiAxLSBccHJvZF97aT0xfV5tIFx0ZXh0e1B9XGxlZnRbXHRleHR7bm90IHJlamVjdCB9SF97MGl9IFxtaWQgSF8wXHJpZ2h0XSBcXAomPSYgMS0gKDEtXGFscGhhKV5tIC4KXGVuZHthcnJheX0KXF0KCiBFeGFtcGxlczoKCiAgJFxhbHBoYT0wLjA1JCBhbmQgJG09NSQ6IEZXRVIkPTAuMjMkCgogJFxhbHBoYT0wLjA1JCBhbmQgJG09MTAwJDogRldFUiQ9MC45OSQKCiAkXGFscGhhPTAuMDUkIGFuZCAkbT0xNTQ0MyQ6IEZXRVIkXGFwcHJveCAxJC4KCi0tLQoKIFRoZXNlIGNhbGN1bGF0aW9ucyBpbGx1c3RyYXRlIHRoZSBwcm9ibGVtIG9mIG11bHRpcGxpY2l0eTogdGhlIG1vcmUgdGVzdHMgdGhhdCBhcmUgcGVyZm9ybWVkLCB0aGUgbGFyZ2VyIHRoZSBwcm9iYWJpbGl0eSB0aGF0IGF0IGxlYXN0IG9uZSBmYWxzZSBwb3NpdGl2ZSBjb25jbHVzaW9uIGlzIG9idGFpbmVkLiBUaHVzIGlmIGFsbCBzaWduaWZpY2FudCByZXN1bHRzIGFyZSBsaXN0ZWQsIGFuZCBzdXBwb3NlIHRoYXQgYWxsIG51bGwgaHlwb3RoZXNlcyBob2xkIHRydWUsIHRoZW4gdGhlIEZXRVIgaXMgdGhlIHByb2JhYmlsaXR5IHRoYXQgYXQgbGVhc3Qgb25lIG9mIHRoZSBsaXN0ZWQgcG9zaXRpdmUgcmVzdWx0cyBpcyBhIGZhbHNlIHBvc2l0aXZlLiBTb21ldGltZXMsIGEgbGlzdCBvZiBzaWduaWZpY2FudCByZXN1bHRzIHJlcHJlc2VudCB0aGUgImRpc2NvdmVyaWVzIiBmcm9tIHRoZSBzdHVkeSwgYW5kIHRoZXJlZm9yZSBhIGZhbHNlIHBvc2l0aXZlIHJlc3VsdCBpcyBvZnRlbiBhbHNvIHJlZmVycmVkIHRvIGFzIGEgZmFsc2UgZGlzY292ZXJ5LgoKRm9yIGV4YW1wbGUsIHdpdGggJG09MTAwJCBhbmQgJFxhbHBoYT0wLjA1JCB0aGUgY2hhbmNlIHRoYXQgYXQgbGVhc3Qgb25lIG9mIHRoZSAiZGlzY292ZXJpZXMiIGlzIGZhbHNlLCBpcyBhYm91dCAkOTlcJSQuIEV2ZW4gd29yc2UsIHdpdGggJG1cYXBwcm94IDE1MDAwJCB0aGUgRldFUiBpbmNyZWFzZXMgdG8gdmlydHVhbGx5ICQxMDBcJSQuIEluIGdlbmVyYWwgd2UgYWxzbyBleHBlY3QgdGhhdCBsaXN0cyBvZiBzaWduaWZpY2FudCByZXN1bHRzIChkaXNjb3ZlcmllcykgZ2V0IGxvbmdlciB3aXRoIGluY3JlYXNpbmcgJG0kLgoKTWFueSByZXNlYXJjaGVycywgaG93ZXZlciwgd2hlbiBwcmVzZW50ZWQgYSBsb25nIGxpc3Qgb2Ygc2lnbmlmaWNhbnQgcmVzdWx0cyAob3IgZGlzY292ZXJpZXMpLCB3b3VsZCBub3QgbWluZCB0b28gbXVjaCBpZiBvbmUgb3IgYSBmZXcgZmFsc2UgZGlzY292ZXJpZXMgYXBwZWFyIGluIHRoZSBsaXN0LiBIZW5jZSwgdGhlIEZXRVIgaXMgbm90IHRoZSBtb3N0IHJlbGV2YW50IHJpc2sgbWVhc3VyZSwgYXMgdGhlIEZXRVIgaXMgYWxsb3dlZCB0byBiZSAkMTAwXCUkIGluIGNhc2UgcmVzZWFyY2hlcnMgZG8gbm90IG1pbmQgdG8gaGF2ZSBhIGZldyBmYWxzZSBkaXNjb3ZlcmllcyBhbW9uZyB0aGUgKHBlcmhhcHMgbWFueSkgcG9zaXRpdmUgcmVzdWx0cyBpbiB0aGUgbGlzdCBvZiBkaXNjb3Zlcmllcy4gQSBiZXR0ZXIgc29sdXRpb24gd2lsbCBiZSBnaXZlbiBsYXRlciwgYnV0IGZpcnN0IHdlIGNvbnRpbnVlIHdpdGggdGhlIHVzZSBvZiBGV0VSLgoKLS0tCgojIyMgSW52ZXJ0IEZXRVIgdG8gc2lnbmlmaWNhbnQgbGV2ZWwgZm9yIGluZGl2aWR1YWwgdGVzdAoKVGhlIGlkZW50aXR5IEZXRVIkPTEtICgxLVxhbHBoYSlebSQgbWF5IGJlIGludmVydGVkIHRvIGZpbmQgdGhlIHNpZ25pZmljYW5jZSBsZXZlbCBhdCB3aGljaCBlYWNoIGluZGl2aWR1YWwgdGVzdCBzaG91bGQgYmUgdGVzdGVkIHRvIGF0dGFpbiB0aGUgbm9taW5hbCBmYW1pbHl3aXNlIGVycm9yIHJhdGUgYXQgRldFUiwKXFsKICAgXGFscGhhID0gMS0oMS1cdGV4dHtGV0VSfSleezEvbX0KXF0Kc28gdGhhdCB0aGUgc2ltdWx0YW5lb3VzIHRlc3RpbmcgcHJvY2VkdXJlIGNvbnRyb2xzIHRoZSBGV0VSIGF0IHRoZSBkZXNpcmVkIGxldmVsIChtZXRob2Qgb2YgU2lkw6BrKS4KCkV4YW1wbGVzOgoKRldFUiQ9MC4wNSQgYW5kICRtPTUkOiAkXGFscGhhPTAuMDEwMiQKCkZXRVIkPTAuMDUkIGFuZCAkbT0xMDAkOiAkXGFscGhhPTAuMDAwNTEkCgpGV0VSJD0wLjA1JCBhbmQgJG09MTU0NDMkOiAkXGFscGhhPTAuMDAwMDAzMyQuCgpXZSB3aWxsIGFyZ3VlIHRoYXQgdGhpcyBwcm9jZWR1cmUgaXMgdG9vIHN0cmluZ2VudCBmb3IgbGFyZ2UgJG0kLgoKLS0tCgpUbyBhdHRhaW4gdGhlIGZhbWlseXdpc2UgZXJyb3IgcmF0ZSBhdCBsZXZlbCBGV0VSIHRoZSBpbmRpdmlkdWFsIGh5cG90aGVzZXMgc2hvdWxkIGJlIHRlc3RlZCBhdCB2ZXJ5IHN0cmluZ2VudCBzaWduaWZpY2FuY2UgbGV2ZWxzIHdoZW4gJG0kIGlzIGxhcmdlLiBUaGUgY29uc2VxdWVuY2Ugb2YgdGVzdGluZyBhdCBhIHNtYWxsIHNpZ25pZmljYW5jZSBsZXZlbCAkXGFscGhhJCBpcyB0aGF0IGl0IGlzIGhhcmQgdG8gZmluZCBzaWduaWZpY2FudCByZXN1bHRzLCBhbmQgdGh1cyB0aGUgbGlzdHMgb2Ygc2lnbmlmaWNhbnQgcmVzdWx0cyAoZGlzY292ZXJpZXMpIGlzIGxpa2VseSB0byBiZSBzaG9ydC4gQ29udHJvbGxpbmcgdGhlIEZXRVIgbWVhbnMgdGhhdCB0aGUgY2hhbmNlIGlzIHNtYWxsIHRoYXQgdGhlc2UgbGlzdHMgY29udGFpbiBvbmUgb3IgbW9yZSBmYWxzZSBwb3NpdGl2ZXMuIEEgbmVnYXRpdmUgY29uc2VxdWVuY2UsIGhvd2V2ZXIsIGlzIHRoYXQgbWFueSBvZiB0aGUgdHJ1ZSBwb3NpdGl2ZSBoeXBvdGhlc2lzIChpLmUuICRIXzEkIGlzIHRydWUpIHdpbGwgbm90IGFwcGVhciBpbiB0aGVzZSBzaG9ydCBsaXN0cy4gSGVuY2UsIHRoZSAicG93ZXIiIGlzIHNtYWxsIChwb3dlciBpcyBub3Qgd2VsbCBkZWZpbmVkIGluIHRoaXMgbXVsdGlwbGUgdGVzdGluZyBzZXR0aW5nIC0tIGV4dGVuc2lvbnMgb2YgdGhlIGNvbmNlcHQgYXJlIHBvc3NpYmxlKS4gVGh1cywgYXZvaWRpbmcgZmFsc2UgcG9zaXRpdmVzIGJ5IGNvbnRyb2xsaW5nIHRoZSBGV0VSIGNvbWVzIGF0IGEgcHJpY2U6IG1hbnkgb2YgdGhlIHRydWUgcG9zaXRpdmUgaHlwb3RoZXNpcyBtYXkgYmUgbWlzc2VkLgoKIyMjIEFkanVzdGVkIHAtdmFsdWUKCkZpcnN0IHdlIGdpdmUgYSB2ZXJ5IGdlbmVyYWwgZGVmaW5pdGlvbiBvZiBhbiAqKmFkanVzdGVkICRwJC12YWx1ZSoqLgoKIERlZmluZSB0aGUgYWRqdXN0ZWQgJHAkLXZhbHVlIGFzCiBcWwogICBcdGlsZGV7cH1faSA9IFx7XGluZiBcYWxwaGFcaW5bMCwxXTogXHRleHR7IHJlamVjdCB9SF97MGl9IFx0ZXh0eyBhdCBGV0VSIH0gXGFscGhhXH0gLgogXF0KIFdpdGggdGhlc2UgYWRqdXN0ZWQgJHAkLXZhbHVlLCB0aGUgJGkkdGggcGFydGlhbCBudWxsIGh5cG90aGVzaXMgbWF5ICBiZSByZWplY3RlZCB3aGVuCiBcWwogICBcdGlsZGV7cH1faSA8IFxhbHBoYQogXF0KIHdoaWxlIGNvbnRyb2xsaW5nIHRoZSBGV0VSIGF0ICRcYWxwaGEkLgoKIFRoZSBjb3JyZWN0ZWQgJHAkLXZhbHVlIHNob3VsZCBiZSByZXBvcnRlZC4gSXQgYWNjb3VudHMgZm9yIHRoZSBtdWx0aXBsaWNpdHkgcHJvYmxlbSBhbmQgaXQgY2FuIGJlIGNvbXBhcmVkIGRpcmVjdGx5IHRvIHRoZSBub21pbmFsIEZXRVIgbGV2ZWwgdG8gbWFrZSBjYWxscyBhdCB0aGUgRldFUiBsZXZlbC4KCi0tLQoKIyBGYWxzZSBEaXNjb3ZlcnkgUmF0ZQoKIyMgSW50cm9kdWN0aW9uCgpUaGUgdGFibGUgc2hvd3MgdGhlIHJlc3VsdHMgb2YgJG0kIGh5cG90aGVzaXMgdGVzdHMgaW4gYSBzaW5nbGUgZXhwZXJpbWVudC4KCnwgICAgICAgICAgICAgICAgICAgICAgICAgIHwgYWNjZXB0ICRIX3swaX0kIHwgcmVqZWN0ICRIX3swaX0kIHwgVG90YWwgfAp8LS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS18LS0tLS0tLS0tLS0tLS0tLS18LS0tLS0tLS0tLS0tLS0tLS0tfC0tLS0tLS18CnwgIG51bGwgICAgIHwgVE4gICAgICAgICAgICAgIHwgRlAgICAgICAgICAgICAgIHwgJG1fMCQgfAp8ICBub24tbnVsbCB8IEZOICAgICAgICAgICAgICB8IFRQICAgICAgICAgICAgICB8ICRtXzEkIHwKfCBUb3RhbCAgICAgICAgICAgICAgICAgICAgfCBOUiAgICAgICAgICAgICAgfCBSICAgICAgICAgICAgICAgfCBtICAgICB8CgoKLSAkVE4kOiBudW1iZXIgb2YgdHJ1ZSBuZWdhdGl2ZTogcmFuZG9tIGFuZCB1bm9ic2VydmVkCi0gJEZQJDogbnVtYmVyIG9mIGZhbHNlIHBvc2l0aXZlczogcmFuZG9tIGFuZCB1bm9ic2VydmVkCi0gJEZOJDogbnVtYmVyIG9mIGZhbHNlIG5lZ2F0aXZlczogcmFuZG9tIGFuZCB1bm9ic2VydmVkCi0gJFRQJDogbnVtYmVyIG9mIHRydWUgcG9zaXRpdmVzOiByYW5kb20gYW5kIHVub2JzZXJ2ZWQKLSAkTlIkOiBudW1iZXIgb2YgYWNjZXB0YW5jZXMgKG5lZ2F0aXZlIHJlc3VsdHMpOiByYW5kb20gYW5kIG9ic2VydmVkCi0gJFIkOiBudW1iZXIgb2YgcmVqZWN0aW9ucyAocG9zaXRpdmUgcmVzdWx0cyk6IHJhbmRvbSBhbmQgb2JzZXJ2ZWQKLSAkbV8wJCBhbmQgJG1fMSQ6IGZpeGVkIGFuZCB1bm9ic2VydmVkCi0gJG0kOiBmaXhlZCBhbmQgb2JzZXJ2ZWQKCi0tLQoKTm90ZSB0aGF0IHRoZSB0YWJsZSBpcyBub3QgY29tcGxldGVseSBvYnNlcnZhYmxlLiBJdCBpcyBpbnRyb2R1Y2VkIHRvIGJldHRlciB1bmRlcnN0YW5kIHRoZSBjb25jZXB0IG9mIEZXRVIgYW5kIHRvIGludHJvZHVjZSB0aGUgY29uY2VwdCBvZiB0aGUgZmFsc2UgZGlzY292ZXJ5IHJhdGUgKEZEUikuCgotLS0KCnwgICAgICAgICAgICAgICAgICAgICAgICAgIHwgYWNjZXB0ICRIX3swaX0kIHwgcmVqZWN0ICRIX3swaX0kIHwgVG90YWwgfAp8LS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS18LS0tLS0tLS0tLS0tLS0tLS18LS0tLS0tLS0tLS0tLS0tLS0tfC0tLS0tLS18CnwgIG51bGwgICAgIHwgVE4gICAgICAgICAgICAgIHwgRlAgICAgICAgICAgICAgIHwgJG1fMCQgfAp8ICBub24tbnVsbCB8IEZOICAgICAgICAgICAgICB8IFRQICAgICAgICAgICAgICB8ICRtXzEkIHwKfCBUb3RhbCAgICAgICAgICAgICAgICAgICAgfCBOUiAgICAgICAgICAgICAgfCBSICAgICAgICAgICAgICAgfCBtICAgICB8CgpUaGUgRldFUiBjYW4gbm93IGJlIHJlZXhwcmVzc2VkIGFzCiBcWwogICBcdGV4dHtGV0VSfT1cdGV4dHtQfVxsZWZ0W1x0ZXh0e3JlamVjdCBhdCBsZWFzdCBvbmUgfUhfezBpfSBcbWlkIEhfMFxyaWdodF0gPSBcdGV4dHtQfVxsZWZ0W0ZQPjBccmlnaHRdIC4KIFxdCgpUaGUgKipGYWxzZSBEaXNjb3ZlcnkgUmF0ZSAoRkRSKSoqIGlzIGRlZmluZWQgYXMKXFsKICAgXHRleHR7RkRSfSA9IFx0ZXh0e0V9XGxlZnRbXGZyYWN7RlB9e1J9XHJpZ2h0XSA9XHRleHR7RX1cbGVmdFtcdGV4dHtGRFB9XHJpZ2h0XQpcXQogd2l0aCAkRlAvUj1cdGV4dHtGRFB9JCB0aGUgKipmYWxzZSBkaXNjb3ZlcnkgcHJvcG9ydGlvbioqIGFuZCBpcyBhbHNvIHJlZmVycmVkIHRvIGFzIHRoZSBmYWxzZSBwb3NpdGl2ZSBwcm9wb3J0aW9uIChGUFApLgoKIEJlZm9yZSBwcm92aWRpbmcgbW9yZSBkZXRhaWxzIG9uIGhvdyB0byBwZXJmb3JtIG11bHRpcGxlIGh5cG90aGVzaXMgdGVzdGluZyB3aXRoIGNvbnRyb2wgb2YgdGhlIEZEUiwgd2UgaWxsdXN0cmF0ZSB0aGUgZGlmZmVyZW5jZSBiZXR3ZWVuIEZXRVIgYW5kIEZEUiBjb250cm9sLgoKLS0tCgpUaGUgZXhhbXBsZXMgaWxsdXN0cmF0ZSB0aGUgcHJvYmxlbSB3aGVuIGNvbnRyb2xsaW5nIHRoZSBGV0VSIGFuZCB0aGV5IGRlbW9uc3RyYXRlIHRoZSBtZWFuaW5nIG9mIHRoZSBGRFIgYXMgYSBtb3JlIHJlYWxpc3RpYyByaXNrIG1lYXN1cmUuCgpUaGUgaWRlYSBpcyB0byBsb29rIGF0IHRhYmxlcyBhcyBvbiB0aGUgcHJldmlvdXMgc2xpZGUsIGJ1dCBmcm9tIG1hbnkgXGVtcGh7cmVwZWF0ZWQgZXhwZXJpbWVudHN9LiBJbiBpdHMgbW9zdCByZXN0cmljdGl2ZSBpbnRlcnByZXRhdGlvbiwgcmVwZWF0ZWQgZXhwZXJpbWVudHMgcmVmZXIgdG8gcmVwbGljYXRpbmcgdGhlIHNhbWUgZXhwZXJpbWVudCBtYW55IHRpbWVzLiBJbiB0aGUgY2FzZSBzdHVkeSAoYnJhaW5zY2FuKSB0aGlzIG1lYW5zIHRoYXQgZWFjaCByZXBlYXRlZCBleHBlcmltZW50IGNvbnNpc3RzIG9mIHJhbmRvbWx5IHNhbXBsaW5nIDYgbm9ybWFsIGFuZCA2IGR5c2xlY3RpYyBjaGlsZHJlbiBmcm9tIGEgcG9wdWxhdGlvbi4gVGhpcyBpcyBvZiBjb3Vyc2UgbW9yZSBsaWtlIGEgdGhvdWdodCBleHBlcmltZW50LiBUaGUgaW50ZXJwcmV0YXRpb24gb2YgdGhlIEZXRVIgYW5kIEZEUiBpbiB0ZXJtcyBvZiByZXBlYXRlZCBleHBlcmltZW50cywgaG93ZXZlciwgIHJlbGF0ZXMgdG8gcmVsYXRpdmUgZnJlcXVlbmNpZXMgYW5kIGF2ZXJhZ2VzIG92ZXIgdGFibGVzIChhcyBvbiB0aGUgcHJldmlvdXMgc2xpZGUpIHJlc3VsdGluZyBmcm9tIGRpZmZlcmVudCBpbmRlcGVuZGVudCBleHBlcmltZW50cy4gRm9yIGV4YW1wbGVzLCBzdXBwb3NlIHRoYXQgeW91IHdvcmsgZm9yIGEgYmlvdGVjaCBjb21wYW55LCBhbmQgZWFjaCB3ZWVrIGEgbWljcm9hcnJheSBleHBlcmltZW50IGlzIHBlcmZvcm1lZCAoZWFjaCBleHBlcmltZW50IGlzIGJhc2VkIG9uIGUuZy4gMjAgYmlvbG9naWNhbCBzYW1wbGVzIGFuZCBhaW1zIGF0IHRlc3RpbmcgZGlmZmVyZW50aWFsIGV4cHJlc3Npb24gZm9yIDEwLDAwMCBnZW5lcykgYW5kIGZvciBlYWNoIG9mIHN1Y2ggZXhwZXJpbWVudHMgeW91IGNhbiB0aGluayBvZiBhIHRhYmxlIGFzIG9uIHRoZSBwcmV2aW91cyBzbGlkZS4gVGhlIEZXRVIgYW5kIEZEUiBtYXkgdGhlbiBhbHNvIGJlIGludGVycHJldGVkIGFzIHJlbGF0aXZlIGZyZXF1ZW5jaWVzIGFuZCBhdmVyYWdlcyBvdmVyIHRhYmxlcyBvZiB0aGVzZSBleHBlcmltZW50cy4gVGhlc2UgZXhwZXJpbWVudHMgYXJlIG5vdCBsaXRlcmFsbHkgInJlcGVhdGVkIiBleHBlcmltZW50cywgYnV0IHN0aWxsIHRoZSBGV0VSIGFuZCBGRFIgcmV0YWluIHRoZWlyIGludGVycHJldGF0aW9uLgoKLS0tCgpGV0VSIGNvbnRyb2wgYXQgJDVcJSQuIFN1cHBvc2UgMTAwIHJlcGVhdGVkIGV4cGVyaW1lbnRzIGhhdmUgYmVlbiBwZXJmb3JtZWQuIEVhY2ggZXhwZXJpbWVudCBjb25zaXN0cyBvZiAkMTAsMDAwJCBoeXBvdGhlc2lzIHRlc3RzLiBTdXBwb3NlIHRoYXQgd2Uga25vdyAodGhpcyBpcyBob3dldmVyIGlycmVhbGlzdGljKSB0aGF0ICQxLDUwMCQgbnVsbCBoeXBvdGhlc2VzIGFyZSBub3QgdHJ1ZS4gQmVsb3cgeW91IHNlZSBhIGZldyBoeXBvdGhldGljYWwgcmVzdWx0cy4KCnwgQS4gICB8IGFjY2VwdCAkSF97MGl9JCAgICAgfCByZWplY3QgJEhfezBpfSQgIHwgIFRvdC4gfAp8IC0tLSB8IC0tLSB8IC0tLSB8IC0tLSB8CnwgdHJ1ZSBOIHwgJDg0OTkkIHwgJDEkIHwgJDg1MDAkIHwKfCB0cnVlIFAgfCAkMTQ5MCQgfCAkMTAkIHwgJDE1MDAkIHwKfCBUb3RhbCAgfCAkOTk4OSQgfCAkMTEkIHwgJDEwMDAwJCB8Cgp8IEIuICAgfCBhY2NlcHQgJEhfezBpfSQgICAgIHwgcmVqZWN0ICRIX3swaX0kICB8ICBUb3QuIHwKfCAtLS0gfCAtLS0gfCAtLS0gfCAtLS0gfAp8IHRydWUgTiB8ICQ4NDk3JCB8ICQzJCB8ICQ4NTAwJCB8CnwgdHJ1ZSBQIHwgJDE0NjMkIHwgJDM3JCB8ICQxNTAwJCB8CnwgVG90YWwgIHwgJDk5NjAkIHwgJDQwJCB8ICQxMDAwMCQgfAoKfCBDLiAgIHwgYWNjZXB0ICRIX3swaX0kICAgICB8IHJlamVjdCAkSF97MGl9JCAgfCAgVG90LiB8CnwgLS0tIHwgLS0tIHwgLS0tIHwgLS0tIHwKfCB0cnVlIE4gfCAkODUwMCQgfCAkMCQgfCAkODUwMCQgfAp8IHRydWUgUCB8ICQxNDk4JCB8ICQyJCB8ICQxNTAwJCB8CnwgVG90YWwgIHwgJDk5OTgkIHwgJDIkIHwgJDEwMDAwJCB8CgoKLSBXaGVuIGNvbnRyb2xsaW5nIHRoZSBGV0VSIGF0ICQ1XCUkLCByZXN1bHRzIGFzIGluIHRoZSB0YWJsZXMgQSBhbmQgQiBhcmUgb25seSBhbGxvd2VkIGluICQ1XCUkIG9mIHRoZQpleHBlcmltZW50cy4KLSBUaGUgb3RoZXIgJDk1XCUkIG9mIHRoZSBleHBlcmltZW50cyByZXN1bHQgaW4gdGFibGVzIHNpbWlsYXIgdG8gIEMuCgotLS0KCi0gVGFibGUgQSBnaXZlcyBhIGxpc3Qgb2YgMTEgZGlzY292ZXJpZXMsIGFuZCB0YWJsZSBCIGdpdmVzIDQwIGRpc2NvdmVyaWVzLiBCb3RoIGxpc3RzIG9mIGRpc2NvdmVyaWVzIGNvbnRhaW4gYXQgbGVhc3QgMSBmYWxzZSBwb3NpdGl2ZS4KLSBUYWJsZSBDIGdpdmVzIGEgc2hvcnRlciBsaXN0IHdpdGggb25seSAyIGRpc2NvdmVyaWVzIHdoaWNoIGFyZSBib3RoIHRydWUgcG9zaXRpdmVzLgoKLSBXaGVuIGNvbnRyb2xsaW5nIHRoZSBGV0VSIGF0ICQ1XCUkLCB0YWJsZXMgd2l0aCBubyBmYWxzZSBkaXNjb3ZlcmllcyAodGFibGUgQykgc2hvdWxkIG1ha2UgdXAgJDk1XCUkIG9mIGFsbCByZXBlYXRlZCBleHBlcmltZW50cy4KLSBIZW5jZSwgd2hlbiBjb250cm9sbGluZyB0aGUgRldFUiBhdCAkNVwlJCBtb3N0IGV4cGVyaW1lbnRzIHdpbGwgaGF2ZSB0byByZXN1bHQgaW4gc2hvcnQgbGlzdHMuCi0gTG9uZ2VyIGxpc3RzIGltcGx5IGEgbGFyZ2VyIHJpc2sgb2YgZmFsc2UgcG9zaXRpdmVzIHNvIHRoYXQgdGhlIEZXRVIgY2Fubm90IGJlIGNvbnRyb2xsZWQgYXQgYSBsZXZlbCBhcyBsb3cgYXMgJDVcJSQuCgotLS0KCkZEUiBjb250cm9sIGF0ICQ1XCUkLiBTdXBwb3NlIDEwMCByZXBlYXRlZCBleHBlcmltZW50cyBoYXZlIGJlZW4gcGVyZm9ybWVkLiBFYWNoIGV4cGVyaW1lbnQgY29uc2lzdHMgb2YgJDEwLDAwMCQgaHlwb3RoZXNpcyB0ZXN0cy4gU3VwcG9zZSB0aGF0IHdlIGtub3cgKHRoaXMgaXMgaG93ZXZlciBpcnJlYWxpc3RpYykgdGhhdCAkMSw1MDAkIG51bGwgaHlwb3RoZXNlcyBhcmUgbm90IHRydWUuIEJlbG93IHlvdSBzZWUgYSBmZXcgaHlwb3RoZXRpY2FsIHJlc3VsdHMuCgp8IEEuICAgfCBhY2NlcHQgJEhfezBpfSQgICAgIHwgcmVqZWN0ICRIX3swaX0kICB8ICBUb3QuIHwKfCAtLS0gfCAtLS0gfCAtLS0gfCAtLS0gfAp8IHRydWUgTiB8ICQ4NDkxJCB8ICQ5JCB8ICQ4NTAwJCB8CnwgdHJ1ZSBQIHwgJDE0NDQkIHwgJDU2JCB8ICQxNTAwJCB8CnwgVG90YWwgIHwgJDk5MzUkIHwgJDY1JCB8ICQxMDAwMCQgfAoKfCBCLiAgIHwgYWNjZXB0ICRIX3swaX0kICAgICB8IHJlamVjdCAkSF97MGl9JCAgfCAgVG90LiB8CnwgLS0tIHwgLS0tIHwgLS0tIHwgLS0tIHwKfCB0cnVlIE4gfCAkODQ5NyQgfCAkMyQgfCAkODUwMCQgfAp8IHRydWUgUCB8ICQxNDEyJCB8ICQ4OCQgfCAkMTUwMCQgfAp8IFRvdGFsICB8ICQ5OTA5JCB8ICQ5MSQgfCAkMTAwMDAkIHwKCnwgQy4gICB8IGFjY2VwdCAkSF97MGl9JCAgICAgfCByZWplY3QgJEhfezBpfSQgIHwgIFRvdC4gfAp8IC0tLSB8IC0tLSB8IC0tLSB8IC0tLSB8CnwgdHJ1ZSBOIHwgJDg1MDAkIHwgJDAkIHwgJDg1MDAkIHwKfCB0cnVlIFAgfCAkMTQ5OCQgfCAkMiQgfCAkMTUwMCQgfAp8IFRvdGFsICB8ICQ5OTk4JCB8ICQyJCB8ICQxMDAwMCQgfAoKCldoZW4gY29udHJvbGxpbmcgdGhlIEZEUiBhdCAkNVwlJCwKCi0gcmVzdWx0cyBhcyBpbiB0aGUgdGFibGVzIEEgYW5kIEIgYXJlIHZlcnkgY29tbW9uLCBidXQKLSByZXN1bHRzIGFzIGluIHRhYmxlIEMgYXJlCnJhdGhlciByYXJlLgotIE9uIG92ZXJhZ2UgJEZQL1IkIGlzIGV4cGVjdGVkIHRvIGJlICQ1XCUkLgoKCi0tLQoKVGFibGVzIEEgYW5kIEIgbm93IGdpdmUgbGlzdHMgd2l0aCA2NSBhbmQgOTEgZGlzY292ZXJpZXMsIHJlc3BlY3RpdmVseS4gQm90aCBsaXN0cyBjb250YWluIGZhbHNlIGRpc2NvdmVyaWVzOiAkOS82NT0xMy44XCUkIGZhbHNlIGRpc2NvdmVyeSBwcm9wb3J0aW9uIGZvciB0YWJsZSBBIGFuZCAkMy85MT0zLjNcJSQgZmFsc2UgZGlzY292ZXJ5IHByb3BvcnRpb24gZm9yIHRhYmxlIEIuIFRhYmxlIEMgZ2l2ZXMgYSBzaG9ydCBsaXN0IHdpdGggbm8gZmFsc2UgZGlzY292ZXJpZXMsIGkuZS4gJDBcJSQgZmFsc2UgZGlzY292ZXJpZXMgcHJvcG9ydGlvbi4gV2hlbiBjb250cm9sbGluZyB0aGUgRkRSIGF0ICQ1XCUkIGVhY2ggbGlzdCBtYXkgY29udGFpbiBmYWxzZSBkaXNjb3ZlcmllcywgYXZlcmFnZWQgb3ZlciByZXBlYXRlZCBleHBlcmltZW50cyB0aGUgZmFsc2UgZGlzY292ZXJpZXMgcHJvcG9ydGlvbiBtdXN0IGJlIGVxdWFsIHRvICQ1XCUkIChkZWZpbml0aW9uIG9mIGZhbHNlIGRpc2NvdmVyeSByYXRlKS4gSGVuY2UsIG1hbnkgdGFibGVzIGFzIHRhYmxlcyBBIGFuZCBCIGFyZSBhbGxvd2VkLiBUaGVyZSBpcyB0aHVzIG5vIG5lZWQgdG8gaGF2ZSBtYW55IHRhYmxlcyBhcyB0YWJsZSBDLgoKSW4gc3VtbWFyeTogY29udHJvbGxpbmcgdGhlIEZEUiBhbGxvd3MgZm9yIG1vcmUgZGlzY292ZXJpZXMgKGkuZS4gbG9uZ2VyIGxpc3RzIHdpdGggc2lnbmlmaWNhbnQgcmVzdWx0cyksIHdoaWxlIHRoZSBmcmFjdGlvbiBvZiBmYWxzZSBkaXNjb3ZlcmllcyBhbW9uZyB0aGUgc2lnbmlmaWNhbnQgcmVzdWx0cyBpbiB3ZWxsIGNvbnRyb2xsZWQgb24gYXZlcmFnZS4gQXMgYSBjb25zZXF1ZW5jZSwgbW9yZSBvZiB0aGUgdHJ1ZSBwb3NpdGl2ZSBoeXBvdGhlc2VzIHdpbGwgYmUgZGV0ZWN0ZWQuCgotLS0KCkZXRVIgY29udHJvbCBpcyB0b28gc3RyaW5nZW50OgoKKlNldHRpbmcgMTogQkg5NSB3aXRoIG5vbWluYWwgRkRSIHNldCBhdCAkMTBcJSQgYW5kICRtXzE9NTAwJCoKCkNvbnNpZGVyIDEwIHJlcGVhdGVkIGV4cGVyaW1lbnRzLCB3aXRoIHRoZSBmb2xsb3dpbmcgb3V0Y29tZXMgZm9yICRUUCQgYW5kICRSJAoKfCAkRlAkIHwgMTEgfCAxNCB8IDggIHwgMjAgfCAxMiB8IDAgfCAxMSB8IDEwIHwgMTMgfCAxOCB8CnwgLS0tIHwgLS0tIHwgLS0tIHwgLS0tIHwgLS0tIHwgLS0tIHwgLS0tIHwgLS0tIHwgLS0tIHwgLS0tIHwgLS0tIHwKfCAkUiQgfCAxMDIgfCAxMTAgfCAxMDEgfCAxNTkgfCA4OCB8IDkxIHwgMTAyIHwgMTQwIHwgMTEwIHwgMTcxIHwKCkJhc2VkIG9uIHRoZXNlIDEwIHJlcGVhdGVkIGV4cGVyaW1lbnRzCgotIEZEUiA9ICRcdGV4dHtFfVxsZWZ0W0ZQL1JccmlnaHRdIFxhcHByb3ggOS44XCUkCi0gRldFUiA9ICRcdGV4dHtQfVxsZWZ0W0ZQID4gMCBccmlnaHRdIFxhcHByb3ggOTBcJSQKLSBzZW5zaXRpdml0eSA9ICRcdGV4dHtFfVxsZWZ0W1RQXHJpZ2h0XS9tXzE9XHRleHR7RX1cbGVmdFtSLUZQXHJpZ2h0XS9tXzFcYXBwcm94IDIxXCUkCgotLS0KCipTZXR0aW5nIDI6IEJvbmZlcnJvbmkgd2l0aCBub21pbmFsIEZXRVIgc2V0IGF0ICQxMFwlJCBhbmQgJG1fMT01MDAkKgoKQ29uc2lkZXIgMTAgcmVwZWF0ZWQgZXhwZXJpbWVudHMsIHdpdGggdGhlIGZvbGxvd2luZyBvdXRjb21lcyBmb3IgJFRQJCBhbmQgJFIkOgoKfCAkRlAkIHwgMCB8IDAgfCAzICB8IDAgfCAwIHwgMCB8IDAgfCAwIHwgMCB8IDAgfAp8IC0tLSB8IC0tLSB8IC0tLSB8IC0tLSB8IC0tLSB8IC0tLSB8IC0tLSB8IC0tLSB8IC0tLSB8IC0tLSB8IC0tLSB8CnwgJFIkIHwgMTAgfCAxMSB8IDEwIHwgMTUgfCA5IHwgOSB8IDEzIHwgMTggfCA5IHwgOSB8CgpCYXNlZCBvbiB0aGVzZSAxMCByZXBlYXRlZCBleHBlcmltZW50cwoKCi0gRkRSID0gJFx0ZXh0e0V9XGxlZnRbRlAvUlxyaWdodF0gXGFwcHJveCAzXCUkCi0gRldFUiA9ICRcdGV4dHtQfVxsZWZ0W0ZQID4gMCBccmlnaHRdIFxhcHByb3ggMTBcJSQKLSBzZW5zaXRpdml0eSA9ICRcdGV4dHtFfVxsZWZ0W1RQXHJpZ2h0XS9tXzE9XHRleHR7RX1cbGVmdFtSLUZQXHJpZ2h0XS9tXzEgXGFwcHJveCAyXCUkCgotLS0KClRoZSBmaXJzdCBzZXR0aW5nIHNob3dzIHJlc3VsdHMgZnJvbSBzaW11bGF0aW9ucyBpbiB3aGljaCB0aGUgRkRSIGlzIGNvbnRyb2xsZWQgd2l0aCB0aGUgQkg5NSBtZXRob2QgKHNlZSBmdXJ0aGVyKSBhdCAkMTBcJSQgKHdoaWNoIGlzIGdvb2QpLiBUaGlzIGdpdmVzIGEgc2Vuc2l0aXZpdHkgb2YgJDIxXCUkIChub3QgbGFyZ2UsIGJ1dCByZWFsaXN0aWMgaW4gbGFyZ2Ugc2NhbGUgZ2Vub21pY3Mgc3R1ZGllcykuIFRoZSBGV0VSLCBob3dldmVyLCBpcyB2ZXJ5IGxhcmdlOiA5MFwlLCBidXQgd2UgZG8gbm90IG1pbmQsIGJlY2F1c2Ugd2UgZG8gbm90IG1pbmQgdGhhdCBpbiBhIGxhcmdlIHNjYWxlIHN0dWR5IHdlIGZpbmQgb25lIG9yIG1vcmUgZmFsc2UgZGlzY292ZXJpZXMgKGkuZS4gJFY+MCQpLCBhcyBsb25nIGFzIHRoZSByZWxhdGl2ZSBudW1iZXIgb2YgZmFsc2UgZGlzY292ZXJpZXMgaXMgb24gYXZlcmFnZSB1bmRlciBjb250cm9sIChGRFIgY29udHJvbCkuCgpUaGUgc2Vjb25kIHNldHRpbmcgc2hvd3MgcmVzdWx0cyBmcm9tIHNpbXVsYXRpb25zIGluIHdoaWNoIHRoZSBGV0VSIGlzIGNvbnRyb2xsZWQgYXQgMTBcJS4gVGhlIEZEUiwgb24gdGhlIG90aGVyIGhhbmQsIGlzIG5vdyBhcyBzbWFsbCBhcyAkM1wlJCBhbmQgdGhlIHNlbnNpdGl2aXR5IGFzIHNtYWxsIGFzIDJcJS4gSGVuY2UsIGhhcmRseSBhbnkgdHJ1bHkgREUgZ2VuZSBjYW4gYmUgZGlzY292ZXJlZCwgYW5kLCBhbHNvLCBoYXJkbHkgYW55IGZhbHNlIGRpc2NvdmVyeSB3aWxsIGVuZCB1cCBpbiB0aGUgY29uY2x1c2lvbnMgb2YgdGhlIGRhdGEgYW5hbHlzaXMuIFRoaXMgYW5hbHlzaXMgd2l0aCBGV0VSIGNvbnRyb2wgaXMgdG9vIHN0cmluZ2VudCBpbiBsYXJnZSBzY2FsZSBnZW5vbWljcyBjb250ZXh0LgoKLS0tCgojIyBUaGUgQmVuamFtaW5pIGFuZCBIb2NoYmVyZyAoMTk5NSkgbWV0aG9kCgpQcm9jZWR1cmUgZm9yIGNvbnRyb2xsaW5nIHRoZSBGRFIgYXQgJFxhbHBoYSQ6CgoxLiBMZXQgJHBfeygxKX1cbGVxIFxsZG90cyBcbGVxIHBfeyhtKX0kIGRlbm90ZSB0aGUgb3JkZXJlZCAkcCQtdmFsdWVzLgoKMi4gTGV0ICRrPVxtYXhce2k6IHBfeyhpKX1cbGVxIGkgXGFscGhhL21cfSQsIGkuZS4gJGskIGlzIHRoZSBsYXJnZXN0IGludGVnZXIgc28gdGhhdCAkcF97KGspfVxsZXEgayBcYWxwaGEvbSQuCgozLiBJZiBzdWNoIGEgJGskIGV4aXN0cywgcmVqZWN0IHRoZSAkayQgbnVsbCBoeXBvdGhlc2VzIGFzc29jaWF0ZWQgd2l0aCAkcF97KDEpfSwgXGxkb3RzLCBwX3soayl9JC4KT3RoZXJ3aXNlIG5vbmUgb2YgdGhlIG51bGwgaHlwb3RoZXNlcyBpcyByZWplY3RlZC4KClRoZSBhZGp1c3RlZCAkcCQtdmFsdWUgKGFsc28ga25vd24gYXMgdGhlICRxJC12YWx1ZSBpbiBGRFIgbGl0ZXJhdHVyZSk6CiBcWwogICBxX3soaSl9PVx0aWxkZXtwfV97KGkpfSA9IFxtaW5cbGVmdFtcbWluX3tqPWksXGxkb3RzLCBtfVxsZWZ0KG0gcF97KGopfS9qXHJpZ2h0KSwgMSBccmlnaHRdLgogXF0KCi0tLQoKLSBCZW5qYW1pbmkgYW5kIEhvY2hiZXJnIHB1Ymxpc2hlZCB0aGVpciBtZXRob2QgaW4gMTk5NTsgaXQgd2FzIG9uZSBvZiB0aGUgZmlyc3QgRkRSIGNvbnRyb2wgbWV0aG9kcy4KLSBUaGUgc2FtZSBhdXRob3JzIHB1Ymxpc2hlZCBsYXRlciB5ZXQgb3RoZXIgRkRSIGNvbnRyb2wgbWV0aG9kcy4KLSBGb3IgdGhpcyByZWFzb24gdGhlaXIgMTk5NSBtZXRob2QgaXMgb2Z0ZW4gcmVmZXJyZWQgdG8gYXMgdGhlIEJlbmphbWluaSBhbmQgSG9jaGJlcmcgMTk5NSBtZXRob2QsIG9yIEJIOTUuCi0gQXMgaW5wdXQgdGhlIG1ldGhvZCBvbmx5IG5lZWRzIHRoZSAkcCQtdmFsdWVzIGZyb20gdGhlICRtJCBoeXBvdGhlc2VzIHRlc3RzLgotIFdoZW4gY29udHJvbGxpbmcgRkRSLCB0aGUgYWRqdXN0ZWQgJHAkLXZhbHVlcyBhcmUgb2Z0ZW4gcmVmZXJyZWQgdG8gYXMgJHEkLXZhbHVlcy4KCi0tLQoKRXhhbXBsZTogJG09NSQgYW5kIEZEUiBjb250cm9sbGVkIGF0ICRcYWxwaGE9MC4wNSQuCgp8ICRwX3soaSl9JCAgIHwgJDwkIG9yICQ+JCB8ICRpIFxhbHBoYS9tJCAgICAgICAgICAgIHwgcmVqZWN0PyB8CnwgLS0tIHwgLS0tIHwgLS0tIHwgLS0tIHwKfCAwLjAwMSAgICAgICB8ICQ8JCAgICAgICAgfCAkMVx0aW1lcyAwLjA1LzU9MC4wMSQgICB8IHllcyAgfAp8IDAuMDA3ICAgICAgIHwgJDwkICAgICAgICB8ICQyXHRpbWVzIDAuMDUvNT0wLjAyJCAgIHwgeWVzICB8CnwgMC4wMTQgICAgICAgfCAkPCQgICAgICAgIHwgJDNcdGltZXMgMC4wNS81PTAuMDMkICAgfCB5ZXMgIHwKfCAwLjAzMSAgICAgICB8ICQ8JCAgICAgICAgfCAkNFx0aW1lcyAwLjA1LzU9MC4wNCQgICB8IHllcyAgfAp8IDAuMDQyICAgICAgIHwgJDwkICAgICAgICB8ICQ1XHRpbWVzIDAuMDUvNT0wLjA1JCAgIHwgeWVzICB8CgpOb3RlIHRoYXQgdGhlIGxhc3QgY29sdW1uIGNhbiBvbmx5IGJlIGZpbGxlZCBpbiBhZnRlciB0aGUgbGFyZ2VzdCAkayQgaXMgZGV0ZWN0ZWQhCgotLS0KCkV4YW1wbGU6ICRtID0gNyQgYW5kIEZEUiBjb250cm9sbGVkIGF0ICRcYWxwaGEgPSAwLjA1JC4KCnwgJHBfeyhpKX0kICAgfCAkPCQgb3IgJD4kIHwgJGkgXGFscGhhL20kICAgICAgICAgICAgfCByZWplY3Q/IHwKfCAtLS0gfCAtLS0gfCAtLS0gfCAtLS0gfAp8IDAuMDAxICAgICAgIHwgJDwkICAgICAgICB8ICQxXHRpbWVzIDAuMDUvNz0wLjAwNyQgICB8IHllcyAgfAp8IDAuMDA3ICAgICAgIHwgJDwkICAgICAgICB8ICQyXHRpbWVzIDAuMDUvNz0wLjAxNCQgICB8IHllcyAgfAp8IDAuMDE0ICAgICAgIHwgJDwkICAgICAgICB8ICQzXHRpbWVzIDAuMDUvNz0wLjAyMSQgICB8IHllcyAgfAp8IDAuMDMxICAgICAgIHwgJDwkICAgICAgICB8ICQ0XHRpbWVzIDAuMDUvNz0wLjAyOSQgICB8IHllcyAgfAp8IDAuMDM1ICAgICAgIHwgJDwkICAgICAgICB8ICQ1XHRpbWVzIDAuMDUvNz0wLjAzNiQgICB8IHllcyAgfAp8IDAuMDQ4ICAgICAgIHwgJD4kICAgICAgICB8ICQ2XHRpbWVzIDAuMDUvNz0wLjA0MyQgICB8IG5vICB8CnwgMC4wNTIgICAgICAgfCAkPiQgICAgICAgIHwgJDdcdGltZXMgMC4wNS83PTAuMDUwJCAgIHwgbm8gIHwKCi0tLQoKIyMgSW50dWl0aW9uIG9mIEJIOTU/CgpDb25zaWRlciAkbSA9IDEwLDAwMCQgdGVzdHMKCi0gV2Ugd2lsbCBleHBlY3QgJDAuMDAxIFx0aW1lcyBtXzAkIHRlc3RzIHRvIHJldHVybiBmYWxzZSBwb3NpdGl2ZXMuIEEgY29uc2VydmF0aXZlIGVzdGltYXRlIG9mIHRoZSBudW1iZXIgb2YgZmFsc2UgcG9zaXRpdmVzIHRoYXQgd2UgY2FuIGV4cGVjdCBjYW4gYmUgb2J0YWluZWQgYnkgY29uc2lkZXJpbmcgdGhhdCB0aGUgbnVsbCBoeXBvdGhlc2VzIGFyZSB0cnVlIGZvciBhbGwgZmVhdHVyZXMsICRtXzAgPSBtID0gIDEwMDAwJC4gV2UgdGhlbiB3b3VsZCBleHBlY3QgJDAuMDAxIFx0aW1lcyAxMCwwMDAgPSAxMCQgZmFsc2UgcG9zaXRpdmVzICgkRlA9MTAkKS4KCi0gU3VwcG9zZSB0aGF0IHRoZSByZXNlYXJjaGVyIGZvdW5kIDIwMCBnZW5lcyB3aXRoICRwPDAuMDAxJCAoJFI9MjAwJCkuCgotIFRoZSBwcm9wb3J0aW9uIG9mIGZhbHNlIHBvc2l0aXZlIHJlc3VsdHMgKEZEUCA9IGZhbHNlIHBvc2l0aXZlIHByb3BvcnRpb24pIGFtb25nIHRoZSBsaXN0IG9mICRSPTIwMCQgZ2VuZXMgY2FuIHRoZW4gYmUgZXN0aW1hdGVkIGFzCiBcWwogICBcd2lkZWhhdHtcdGV4dHtGRFB9fT1cZnJhY3tGUH17Un09XGZyYWN7MTB9ezIwMH09XGZyYWN7MC4wMDEgXHRpbWVzIDEwMDAwfXsyMDB9ID0gMC4wNS4KIFxdCgpSZWNhbGwgdGhhdCB0aGUgQlwmSCAoMTk5NSkgcHJvY2VkdXJlIGludm9sdmVzIGZpbmRpbmcgdGhlCmxhcmdlc3QgaW50ZWdlciAkayQgc28gdGhhdCAkcF97KGspfSBcbGVxIGsgXGFscGhhIC9tJCwgb3IsCmVxdWl2YWxlbnRseSwgJHBfeyhrKX0gbSAvayBcbGVxIFxhbHBoYSQuCgpJbiB0aGlzIGV4YW1wbGU6ICRrPTIwMCQsICRwX3soayl9PTAuMDAxJCwgJG09MTAsMDAwJCBhbmQgJFxhbHBoYT0wLjA1JC4KCi0tLQoKIyMjIENvbW1lbnRzCgotIEl0IGlzIGEgKipsaW5lYXIgc3RlcC11cCBwcm9jZWR1cmUqKiA6IGl0IHN0YXJ0cyBmcm9tIHRoZSBsZWFzdCBzaWduaWZpY2FudCByZXN1bHQgKGxhcmdlc3QgcC12YWx1ZSkgYW5kIHN0ZXBzLXVwIHRvIG1vcmUgc2lnbmlmaWNhbnQgcmVzdWx0cyAobG93ZXIgcC12YWx1ZXMpLgotIEluIEZEUiB0ZXJtaW5vbG9neSB0aGUgYWRqdXN0ZWQgJHAkLXZhbHVlIGlzIG9mdGVuIHJlZmVycmVkIHRvIGFzIGEgJHEkLXZhbHVlLgotIFRoZSBCSDk1IG1ldGhvZCBhc3N1bWVzIHRoYXQgYWxsIHRlc3RzIGFyZSBtdXR1YWxseSBpbmRlcGVuZGVudCAob3IgYXQgbGVhc3QgYSBwYXJ0aWN1bGFyIGZvcm0gb2YgcG9zaXRpdmUgZGVwZW5kZW5jZSBiZXR3ZWVuIHRoZSBwLXZhbHVlcykuICAKLSBXaGVuIHRoZSBhc3N1bXB0aW9ucyBob2xkLCBpdCBndWFyYW50ZWVzCiAgXFsKICAgIFx0ZXh0e0ZEUn09XHRleHR7RX1cbGVmdFtUUC9SXHJpZ2h0XT1cdGV4dHtFfVxsZWZ0W1x0ZXh0e0ZEUH1ccmlnaHRdIFxsZXEgXGZyYWN7bV8wfXttfSBcYWxwaGEgXGxlcSBcYWxwaGEgLgogIFxdCgogVGh1cywgaWYgd2Uga25ldyAkbV8wJCAodGhlIG51bWJlciBvZiB0cnVlIG51bGxzKSwgd2UgY291bGQgaW1wcm92ZSB0aGUgbWV0aG9kIGJ5IGFwcGx5aW5nIGl0IHRvIHRoZSBsZXZlbCAkXGFscGhhIG0vbV8wJCAoY2ZyLiBCb25mZXJyb25pKS4KCiAkXGxvbmdyaWdodGFycm93JCBtYW55IEZEUiBtZXRob2RzIGNvbnNpc3QgaW4gZXN0aW1hdGluZyAkbV8wJCBvciB0aGUgZnJhY3Rpb24gb2YgbnVsbCBnZW5lcyAkbV8wL20kLgoKLS0tCgpUaGUgaW5lcXVhbGl0eQpcWwogIFx0ZXh0e0ZEUn0gXGxlcSBcZnJhY3ttXzB9e219IFxhbHBoYSBcbGVxIFxhbHBoYQpcXQpzaG93cyB0aGF0IEJIMTk5NSBpcyBhIGNvbnNlcnZhdGl2ZSBtZXRob2QsIGkuZS4gaXQgY29udHJvbHMgdGhlIEZEUiBhdCB0aGUgc2FmZSBzaWRlLCBpLmUuIHdoZW4gb25lIGlzIHByZXBhcmVkIHRvIGNvbnRyb2wgdGhlIEZEUiBhdCB0aGUgbm9taW5hbCBsZXZlbCAkXGFscGhhJCwgdGhlIEJIOTUgd2lsbCBndWFyYW50ZWUgdGhhdCB0aGUgdHJ1ZSBGRFIgaXMgbm90IGxhcmdlciB0aGFuIHRoZSBub21pbmFsIGxldmVsICh3aGVuIHRoZSBhc3N1bXB0aW9ucyBob2xkKS4KCk1vcmUgaW50ZXJlc3RpbmdseSBpcyB0aGF0ICRcZnJhY3ttXzB9e219IFxhbHBoYSQgaXMgaW4gYmV0d2VlbiB0aGUgdHJ1ZSBGRFIgYW5kIHRoZSBub21pbmFsIEZEUi4gU3VwcG9zZSB0aGF0ICRtXzAkIHdlcmUga25vd24gYW5kIHRoYXQgdGhlIEJIOTUgbWV0aG9kIHdlcmUgYXBwbGllZCBhdCB0aGUgbm9taW5hbCBGRFIgbGV2ZWwgb2YgJFxhbHBoYT1tL21fMCBcYWxwaGFeKiQsIGluIHdoaWNoICRcYWxwaGFeKiQgaXMgdGhlIEZEUiBsZXZlbCB3ZSB3YW50IHRvIGNvbnRyb2wuIFRoZW4gdGhlIGluZXF1YWxpdHkgZ2l2ZXMKXFsKICBcdGV4dHtGRFJ9IFxsZXEgXGZyYWN7bV8wfXttfSBcYWxwaGEgPSBcZnJhY3ttXzB9e219IFxmcmFje219e21fMH1cYWxwaGFeKiA9IFxhbHBoYV4qICwKXF0KYW5kIGhlbmNlIEJIOTUgd291bGQgYmV0dGVyIGNvbnRyb2wgdGhlIEZEUiAgYXQgJFxhbHBoYV4qJC4KCk5vdGUgdGhhdCAkXGFscGhhPW0vbV8wIFxhbHBoYV4qPlxhbHBoYV4qJCBhbmQgaGVuY2UgdGhlIHJlc3VsdHMgaXMgbGVzcyBjb25zZXJ2YXRpdmUgdGhhbiB0aGUgb3JpZ2luYWwgQkg5NSBtZXRob2QuCgotLS0KClRoZSBhYm92ZSByZWFzb25pbmcgaW1wbGllcyBhICoqZ2VuZXJhbGl6ZWQgYWRhcHRpdmUgbGluZWFyIHN0ZXAtdXAgcHJvY2VkdXJlKio6CgotIGVzdGltYXRlICRtXzAkOiAkXGhhdHttfV8wJAotIG9mICRcaGF0e219XzA9MCQsIHJlamVjdCBhbGwgbnVsbCBoeXBvdGhlc2VzOwogb3RoZXJ3aXNlLCBhcHBseSB0aGUgc3RlcC11cCBwcm9jZWR1cmUgb2YgQkggOTUgYXQgdGhlIGxldmVsICRcYWxwaGE9bSBcYWxwaGFeKi9caGF0e219XzAkIHRvIGNvbnRyb2wgdGhlIEZEUiBhdCAkXGFscGhhXiokLgoKVGhlIGFkanVzdGVkICRwJC12YWx1ZXMgKD0kcSQtdmFsdWVzKSBhcmUgb2J0YWluZWQgYXMKXFsKICBcdGlsZGV7cH1feyhpKX0gPSBcZnJhY3tcaGF0e219XzB9e219IFxtaW5cbGVmdFx7XG1pbl97aj1pLFxsZG90cywgbX1ce20gcF97KGopfS9qXH0gLDEgXHJpZ2h0XH0uClxdCgotIE1hbnkgRkRSIHByb2NlZHVyZXMgY2FuIGJlIGZpdCBpbnRvIHRoaXMgZGVmaW5pdGlvbiAoZS5nLiBCZW5qYW1pbmkgYW5kIEhvY2hiZXJnICgyMDAwKSBhbmQgVGlic2hpcmFuaSAoMjAwMykpLgotIFdlIGRvIG5vdCBnaXZlIGRldGFpbHMgb24gdGhlIG1ldGhvZHMgZm9yIGVzdGltYXRpbmcgJG1fMCQsIGJ1dCBzb21lIG9mIHRoZW0gYXJlIGltcGxlbWVudGVkIGluIHRoZSBSIHNvZnR3YXJlLiBPbiB0aGUgbmV4dCBwYWdlIHdlIGlsbHVzdHJhdGUgd2l0aCBzaW11bGF0ZWQgZGF0YSB0aGF0IEJIIGNhbiBiZSBpbXByb3ZlZCB3aXRoIGVzdGltYXRlZCAkbV8wJC4KCi0tLQoKIyMjIE90aGVyIGltcG9ydGFudCBjb25zaWRlcmF0aW9ucwoKLSBJdCBjYW4gYmUgc2hvd24gIHRoYXQgdGhlIEJILUZEUiBtZXRob2Qgd2Vha2x5IGNvbnRyb2xzIHRoZSBGV0VSLCBpLmUuIGl0IGNvbnRyb2xzIHRoZSBGV0VSIGlmIGFsbCBmZWF0dXJlcyBhcmUgZmFsc2UgKCRtXzA9bSQpLgoKLSBUaGUgQkgtRkRSIGlzIGRlcml2ZWQgdW5kZXIgdGhlIGFzc3VtcHRpb24gb2YgaW5kZXBlbmRlbmNlIG9mIHRoZSBmZWF0dXJlcyBhbmQgaGFzIGJlZW4gc2hvd24gdG8gYmUgb25seSB2YWxpZCB1bmRlciBzcGVjaWFsIGZvcm1zIG9mIGRlcGVuZGVuY2UgYmV0d2VlbiB0aGUgZmVhdHVyZXMuCgotLS0KCiMjIEJyYWluIEV4YW1wbGUKCmBgYHtyfQpkdGkgJT4lCiAgZ2dwbG90KGFlcyh4ID0gcC52YWx1ZSkpICsKICBnZW9tX2hpc3RvZ3JhbShjb2xvciA9ICJibGFjayIsYnJlYWtzID0gc2VxKDAsMSwuMDUpKQpgYGAKCi0gVGhlIGdyYXBoIHNob3dzIHRoZSBoaXN0b2dyYW0gb2YgdGhlICRtPTE1NDQzJCAkcCQtdmFsdWVzLiBJdCBzaG93cyBhIGRpc3RyaWJ1dGlvbiB3aGljaCBpcyBjbG9zZSB0byBhIHVuaWZvcm0gZGlzdHJpYnV0aW9uIGZvciB0aGUgbGFyZ2VyIHAtdmFsdWVzLCBidXQgd2l0aCBtb3JlIHNtYWxsICRwJC12YWx1ZXMgdGhhbiBleHBlY3RlZCB1bmRlciBhIHVuaWZvcm0gZGlzdHJpYnV0aW9uLgoKLSBUaGlzIGlzIGEgdHJlbmQgdGhhdCB3b3VsZCBhcmlzZSBpZiBtb3N0IG9mIHRoZSBoeXBvdGhlc2VzIGFyZSBudWxscyAocmVzdWx0aW5nIGluICRwJC12YWx1ZXMgZnJvbSBhIHVuaWZvcm0gZGlzdHJpYnV0aW9uKSwgYnV0IHNvbWUgYXJlIG5vbi1udWxscyAobW9yZSBsaWtlbHkgdG8gcmVzdWx0IGluIHNtYWxsICRwJC12YWx1ZXMpLgoKLS0tCgoKYGBge3J9CmR0aSA8LSBkdGkgJT4lCiAgbXV0YXRlKAogICAgcGFkaiA9IHAuYWRqdXN0KHAudmFsdWUsIG1ldGhvZD0iZmRyIiksCiAgICB6RkRSID0gKHBhZGogPCAwLjA1KSAqIHoudmFsdWUpCgpwUGFkaiA8LSBkdGkgJT4lCiAgZ2dwbG90KGFlcyhwLnZhbHVlLHBhZGopKSArCiAgZ2VvbV9wb2ludCgpICsKICBnZW9tX3NlZ21lbnQoeD0wLHk9MCx4ZW5kPTEseWVuZD0xKSArCiAgeWxhYigiYWRqdXN0ZWQgcC12YWx1ZSAoQkgsIDE5OTUpIikKCmdyaWQuYXJyYW5nZShwUGFkaiwKICBwUGFkaiArIHlsaW0oYygwLDAuMDUpKSwKICBuY29sPTIpCgojIHVuY29ycmVjdGVkIHAtdmFsdWVzCnRhYmxlKGR0aSRwLnZhbHVlIDwgMC4wNSkKCiMgQkggY29ycmVjdGVkIHAtdmFsdWVzCnRhYmxlKGR0aSRwYWRqIDwgMC4wNSkKCmBgYAoKQXQgdGhlIDUlIEZEUiwgYHIgc3VtKGR0aSRwYWRqIDwgMC4wNSlgIHZveGVscyBhcmUgcmV0dXJuZWQgYXMgc2lnbmlmaWNhbnRseSBkaWZmZXJlbnRpYWxseSBhY3RpdmUgYmV0d2VlbiBkeXNsZXhpYyBhbmQgbm9uLWR5c2xleGljIGNoaWxkcmVuLgoKYGBge3J9CnBGRFIgPC0gZHRpICU+JQogIGdncGxvdCgKICAgIGFlcygKICAgICAgY29vcmQueSwKICAgICAgY29vcmQueCwKICAgICAgY29sb3I9ekZEUikKICAgICkgKwogIGdlb21fcG9pbnQoKSArCiAgc2NhbGVfY29sb3VyX2dyYWRpZW50Mihsb3cgPSAiYmx1ZSIsbWlkPSJ3aGl0ZSIsaGlnaD0icmVkIikgKwogIHRyYW5zaXRpb25fbWFudWFsKGNvb3JkLnopICsKICBsYWJzKHRpdGxlID0gInRyYW5zZWN0aW9uIHogPSB7ZnJhbWV9IikgKwogIHRoZW1lX2dyZXkoKQpgYGAKCmBgYHtyIGVjaG8gPSBGQUxTRSwgbWVzc2FnZSA9IEZBTFNFLCBldmFsPWtuaXRyOjppc19odG1sX291dHB1dCgpfQphbmltYXRlKHBGRFIsIG5mcmFtZXMgPSAxMDMsIGVuZF9wYXVzZSA9IDMpCmBgYAoKCiMgbG9jYWwgZmRyCgojIyBJbnRyb2R1Y3Rpb24KClN1cHBvc2UgdGhhdCB0aGUgdGVzdCBzdGF0aXN0aWMgZm9yIHRlc3RpbmcgJEhfezBpfSQgaXMgZGVub3RlZCBieSAkel9pJCwgYW5kIHRoYXQgdGhlIHRlc3Qgc3RhdGlzdGljcyBoYXZlIGEgJE4oMCwxKSQgbnVsbCBkaXN0cmlidXRpb24uCgpJZiBhbGwgJG0kIG51bGwgaHlwb3RoZXNlcyBhcmUgdHJ1ZSwgdGhlIGhpc3RvZ3JhbSBvZiB0aGUgJG0kIHRlc3Qgc3RhdGlzdGljcyBzaG91bGQgYXBwcm94aW1hdGUgdGhlIHRoZW9yZXRpY2FsIG51bGwgZGlzdHJpYnV0aW9uIChkZW5zaXR5ICRmXzAoeikkKS4KCmBgYHtyIGVjaG89RkFMU0V9CnAxCmBgYAoKQXNzdW1pbmcgdGhhdCB0aGUgdGVzdCBzdGF0aXN0aWMgaGFzIGEgc3RhbmRhcmQgbm9ybWFsIG51bGwgZGlzdHJpYnV0aW9uIGlzIG5vdCByZXN0cmljdGl2ZS4gRm9yIGV4YW1wbGUsIHN1cHBvc2UgdGhhdCAkdCQtdGVzdHMgaGF2ZSBiZWVuIGFwcGxpZWQgYW5kIHRoYXQgdGhlIG51bGwgZGlzdHJpYnV0aW9uIGlzICR0X2QkLCB3aXRoICRkJCByZXByZXNlbnRpbmcgdGhlIGRlZ3JlZXMgb2YgZnJlZWRvbS4gTGV0ICRGX3t0ZH0kIGRlbm90ZSB0aGUgZGlzdHJpYnV0aW9uIGZ1bmN0aW9uIG9mICR0X2QkIGFuZCBsZXQgJFxQaGkkIGRlbm90ZSB0aGUgZGlzdHJpYnV0aW9uIGZ1bmN0aW9uIG9mIHRoZSBzdGFuZGFyZCBub3JtYWwgZGlzdHJpYnV0aW9uLiBJZiAkVCQgZGVub3RlcyB0aGUgJHQkLXRlc3Qgc3RhdGlzdGljLCB0aGVuLCB1bmRlciB0aGUgbnVsbCBoeXBvdGhlc2lzLApcWwogIFQgXHNpbSB0X2QKXF0KYW5kIGhlbmNlClxbCiAgRl97dGR9KFQpIFxzaW0gVVswLDFdClxdCmFuZApcWwogIFogPSBcUGhpXnstMX0oRl97dGR9KFQpKSBcc2ltIE4oMCwxKS4KXF0KSWYgYWxsICRtJCBudWxsIGh5cG90aGVzZXMgYXJlIHRydWUsIHRoZW4gZWFjaCBvZiB0aGUgJFpfaSQgaXMgJE4oMCwxKSQgYW5kIHRoZSBzZXQgb2YgJG0kIGNhbGN1bGF0ZWQgJHpfaSQgdGVzdCBzdGF0aXN0aWNzIG1heSBiZSBjb25zaWRlcmVkIGFzIGEgc2FtcGxlIGZyb20gJE4oMCwxKSQuIEhlbmNlLCB1bmRlciB0aGVzZSBjb25kaXRpb25zIHdlIGV4cGVjdCB0aGUgaGlzdG9ncmFtIG9mIHRoZSAkbSQgJHpfaSQncyB0byBsb29rIGxpa2UgdGhlIGRlbnNpdHkgb2YgdGhlIG51bGwgZGlzdHJpYnV0aW9uLgoKIyMgVHdvIGdyb3VwIG1vZGVsCgotIFN1cHBvc2UgdGhhdCB1bmRlciB0aGUgYWx0ZXJuYXRpdmUgaHlwb3RoZXNpcyB0aGUgdGVzdCBzdGF0aXN0aWMgaGFzIGRlbnNpdHkgZnVuY3Rpb24gJGZfMSh6KSQuCgotIFdlIHVzZSB0aGUgdGVybSAibnVsbCIgdG8gcmVmZXIgdG8gYSBjYXNlICRpJCBmb3Igd2hpY2ggJEhfezBpfSQgaXMgdHJ1ZSwgYW5kICJub24tbnVsbCIgZm9yIGEgY2FzZSAkaSQgZm9yIHdoaWNoICRIX3swaX0kIGlzIG5vdCB0cnVlLgoKCi0gQ29uc2lkZXIgdGhlICoqcHJpb3IgcHJvYmFiaWxpdGllcyoqClxbCiAgXHBpXzAgPSBcdGV4dHtQfVxsZWZ0W1x0ZXh0e251bGx9XHJpZ2h0XSBcdGV4dHsgYW5kIH0gXHBpXzE9XHRleHR7UH1cbGVmdFtcdGV4dHtub24tbnVsbH1ccmlnaHRdID0gMS1ccGlfMC4KXF0KCi0gVGhlIG1hcmdpbmFsIGRpc3RyaWJ1dGlvbiBvZiB0aGUgJG0kIHRlc3Qgc3RhdGlzdGljcyBpcyB0aGVuIGdpdmVuIGJ5IHRoZSAqKm1peHR1cmUgZGlzdHJpYnV0aW9uKioKClxbCiAgZih6KSA9IFxwaV8wIGZfMCh6KSArIFxwaV8xIGZfMSh6KQpcXQoKIyMjIEV4YW1wbGVzIG9mIG1peHR1cmUgZGlzdHJpYnV0aW9ucwoKV2UgaGF2ZSBhbHJlYWR5IGV4cGxvcmVkIG1peHR1cmUgZGlzdHJpYnV0aW9ucyBpbiBkZXRhaWwgaW4gdGhlIHBhcGVyIHJlYWRpbmcgc2Vzc2lvbiBvbiBtb2RlbCBiYXNlZCBjbHVzdGVyaW5nLgoKLSBibHVlOiAkZl8wJDogJE4oMCwxKSQsIHJlZDogJGZfMSQ6ICROKDEsMSkkCgpgYGB7cn0KY29tcG9uZW50cyA8LSB0aWJibGUoeiA9IHNlcSgtNiw2LC4wMSkpICU+JQogIG11dGF0ZSgKICAgIGYwID0gZG5vcm0oeiksCiAgICBmMSA9IGRub3JtKHosIG1lYW4gPSAxKSkKCmNvbXBvbmVudHMgJT4lCiAgZ2F0aGVyKGNvbXBvbmVudCwgZGVuc2l0eSwgLXopICU+JQogIGdncGxvdChhZXMoeixkZW5zaXR5LGNvbG9yID0gY29tcG9uZW50KSkgKwogIGdlb21fbGluZSgpICsKICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzPWMoImJsdWUiLCJyZWQiKSkKIGBgYAoKVGhlIGdyYXBocyBzaG93cyB0aGUgdHdvIGNvbXBvbmVudCBkaXN0cmlidXRpb25zIHNlcGFyYXRlbHkuCgoKLS0tCgotIGJsdWU6ICRccGlfMCBcdGltZXMgZl8wJCB3aXRoICRccGlfMD0wLjkkIGFuZCAkZl8wID0gTigwLDEpJAotIHJlZDogJFxwaV8xXHRpbWVzIGZfMSQgd2l0aCAkXHBpXzE9MS1ccGlfMD0wLjEkIGFuZCAkZl8xID0gTigxLDEpJAoKYGBge3J9CnAwIDwtIDAuOQpwMSA8LSAxLXAwCm11MSA8LSAxCnNjYWxlZENvbXBvbmVudHMgPC0gdGliYmxlKHogPSBzZXEoLTYsNiwuMDEpKSAlPiUKICBtdXRhdGUoCiAgICBwMHhmMCA9IGRub3JtKHopICogcDAsCiAgICBwMXhmMSA9IGRub3JtKHosIG1lYW4gPSBtdTEpKnAxCiAgICApCgpzY2FsZWRDb21wb25lbnRzICU+JQogIGdhdGhlcihjb21wb25lbnQsIGRlbnNpdHksIC16KSAlPiUKICBnZ3Bsb3QoYWVzKHosZGVuc2l0eSxjb2xvciA9IGNvbXBvbmVudCkpICsKICBnZW9tX2xpbmUoKSArCiAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcz1jKCJibHVlIiwicmVkIikpICsKICBnZ3RpdGxlKCJTY2FsZWQgY29tcG9uZW50cyIpCmBgYAoKLS0tCgpNaXh0dXJlIGRpc3RyaWJ1dGlvbgoKLSBibHVlOiAkXHBpXzAgXHRpbWVzIGZfMCQgd2l0aCAkXHBpXzA9MC45JCBhbmQgJGZfMCA9IE4oMCwxKSQKLSByZWQ6ICRccGlfMVx0aW1lcyBmXzEkIHdpdGggJFxwaV8xPTEtXHBpXzA9MC4xJCBhbmQgJGZfMSA9IE4oMSwxKSQKLSBibGFjazogJGY9XHBpXzAgZl8wICsgXHBpXzEgZl8xJAoKYGBge3J9CnNjYWxlZENvbXBvbmVudHMgJT4lCiAgbXV0YXRlKGY9cDB4ZjArcDF4ZjEpICU+JQogIGdhdGhlcihjb21wb25lbnQsIGRlbnNpdHksIC16KSAlPiUKICBnZ3Bsb3QoYWVzKHosZGVuc2l0eSxjb2xvciA9IGNvbXBvbmVudCkpICsKICBnZW9tX2xpbmUoKSArCiAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcz1jKCJibGFjayIsImJsdWUiLCJyZWQiKSkgKwogIGdndGl0bGUoIk1peHR1cmUgYW5kIHNjYWxlZCBjb21wb25lbnRzIikKYGBgCgotLS0KCk1peHR1cmUgJFxwaV8wIGZfMCh6KStccGlfMSBmXzEoeikkIHdpdGggJFxwaV8wPTAuNjUkIGFuZCAkZl8xPSBOKDIsMSkkIGFuZCAkZl8wID0gTigwLDEpJAoKYGBge3J9CmBgYHtyfQpwMCA8LSAwLjY1CnAxIDwtIDEtcDAKbXUxIDwtIDIKc2NhbGVkQ29tcG9uZW50cyA8LSB0aWJibGUoeiA9IHNlcSgtNiw2LC4wMSkpICU+JQogIG11dGF0ZSgKICAgIHAweGYwID0gZG5vcm0oeikgKiBwMCwKICAgIHAxeGYxID0gZG5vcm0oeiwgbWVhbiA9IG11MSkqcDEpCgpzY2FsZWRDb21wb25lbnRzICU+JQogIG11dGF0ZShmPXAweGYwK3AxeGYxKSAlPiUKICBnYXRoZXIoY29tcG9uZW50LCBkZW5zaXR5LCAteikgJT4lCiAgZ2dwbG90KGFlcyh6LGRlbnNpdHksY29sb3IgPSBjb21wb25lbnQpKSArCiAgZ2VvbV9saW5lKCkgKwogIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXM9YygiYmxhY2siLCJibHVlIiwicmVkIikpICsKICBnZ3RpdGxlKCJNaXh0dXJlIGFuZCBzY2FsZWQgY29tcG9uZW50cyAocDAgPSAwLjM1KSIpCmBgYAoKIyMjIHNpbXVsYXRpb25zCgpTaW11bGF0ZWQgZGF0YTogMjAwMDAgJHokLXN0YXRpc3RpY3Mgd2l0aCAkXHBpXzE9MC4xMCQgbm9uLW51bGxzIHdpdGggJGZfMT1OKDEsMSkkLgoKYGBge3J9CnAwIDwtIC45CnAxIDwtIDEtcDAKbXUxIDwtIDEKbSA8LSAyMDAwMAoKelNpbSA8LSBjKAogIHJub3JtKG0gKiBwMCksCiAgcm5vcm0obSAqIHAxLCBtZWFuPW11MSkKICApCgp6U2ltICU+JQogIGFzX3RpYmJsZSAlPiUKICBnZ3Bsb3QoYWVzKHggPSB6U2ltKSkgKwogIGdlb21faGlzdG9ncmFtKAogICAgYWVzKHk9Li5kZW5zaXR5Li4pLAogICAgY29sb3IgPSAiYmxhY2siKSArCiAgc3RhdF9mdW5jdGlvbihmdW4gPSBkbm9ybSwKICAgIGFyZ3MgPSBsaXN0KAogICAgICBtZWFuID0gMCwKICAgICAgc2Q9MSksCiAgICBjb2xvcj0iYmx1ZSIpCmBgYAoKSXQgaXMgaGFyZCB0byBzZWUgdGhlIGRpZmZlcmVuY2UgYmV0d2VlbiB0aGUgaGlzdG9ncmFtIGFuZCB0aGUgZGVuc2l0eSBmdW5jdGlvbiBvZiB0aGUgbnVsbCBkaXN0cmlidXRpb24gKGJsdWUgY3VydmUpLCBiZWNhdXNlIHRoZSBtZWFuIG9mICRmXzEkIGlzIG5vdCBtdWNoIGxhcmdlciB0aGFuIDAgYW5kIGJlY2F1c2Ugb25seSAkXHBpXzE9MTBcJSQgbm9uLW51bGxzIGFyZSBpbmNsdWRlZCBhbmQgYmVjYXVzZSB0aGUgYWx0ZXJuYXRpdmUgaXMgbm90IGZhciBmcm9tIHRoZSBudWxsIGRpc3RyaWJ1dGlvbi4gSG93ZXZlciwgdGhpcyBpcyBub3QgYW4gdW5yZWFsaXN0aWMgc2V0dGluZy4KCk5vdGUsIHRoYXQgaW4gbW9zdCBzZXR0aW5ncyB0aGUgbm9uLW51bGwgZmVhdHVyZXMgd2lsbCBvcmlnaW5hdGUgZnJvbSBhIG1peHR1cmUgb2YgbXVsdGlwbGUgZGlzdHJpYnV0aW9ucyB3aXRoIHBvc2l0aXZlIGFuZCBuZWdhdGl2ZSBtZWFucy4KRm9ydHVuYXRlbHksIHRoZSBsb2NhbCBmZHIgbWV0aG9kIGRvZXMgbm90IHJlcXVpcmUgdXMgdG8gZXN0aW1hdGUgJGZfMSQgYXMgd2Ugd2lsbCBzZWUgZnVydGhlci4KCi0tLQoKIyMgbG9jYWwgZmRyCgpXZSBjYW4gbm93IGNhbGN1bGF0ZSB0aGUgcHJvYmFiaWxpdHkgdGhhdCBhIGNhc2UgaXMgYSBudWxsIGdpdmVuIHRoZSBvYnNlcnZlZCAkeiQsClxbCiAgXHRleHR7UH1cbGVmdFtcdGV4dHtudWxsfVxtaWQgelxyaWdodF0gPSBcZnJhY3tccGlfMCBmXzAoeil9e2Yoeil9IC4KXF0KVGhpcyBwcm9iYWJpbGl0eSBpcyByZWZlcnJlZCB0byBhcyB0aGUgKipsb2NhbCBmYWxzZSBkaXNjb3ZlcnkgcmF0ZSoqLCBhbmQgZGVub3RlZCBieSBmZHIkKHopJC4KCklmIGZvciBhbiBvYnNlcnZlZCAkeiQsIGZkciQoeikkIGlzIHN1ZmZpY2llbnRseSBzbWFsbCwgb25lIG1heSBiZWxpZXZlIHRoYXQgdGhlIGNhc2UgaXMgYSB0cnVlIGRpc2NvdmVyeSAoaS5lLiAkSF97MGl9JCBtYXkgYmUgcmVqZWN0ZWQpLgoKIyMjIExpbmsgd2l0aCBGRFIKClJlY2FsbCB0aGUgZGVmaW5pdGlvbiBvZiB0aGUgRkRSLApcYmVnaW57ZXFuYXJyYXl9Clx0ZXh0e0ZEUn0KJj0mIFx0ZXh0e0V9XGxlZnRbRlAvUlxyaWdodF0gXFwKJj0mIFx0ZXh0e0V9XGxlZnRbXHRleHR7bnVtYmVyIG9mIG51bGxzIGFtb25nIHJlamVjdGVkfSAvIFx0ZXh0e251bWJlciBvZiByZWplY3RlZH1ccmlnaHRdIFxcCiY9JiBcdGV4dHtQfVxsZWZ0W1x0ZXh0e251bGx9IFxtaWQgXHRleHR7cmVqZWN0ZWR9XHJpZ2h0XQpcZW5ke2VxbmFycmF5fQoKLS0tCgoKLSBUaGUgRkRSIGlzIHRvIGJlIGludGVycHJldGVkIGFzIGFuIG92ZXJhbGwgcmlzazogKmFtb25nIGFsbCByZWplY3RlZCBoeXBvdGhlc2VzKiAoZGlzY292ZXJpZXMpIGl0IGdpdmVzIHRoZSBleHBlY3RlZCBmcmFjdGlvbiAob3IgcHJvYmFiaWxpdHkpIG9mIGEgbnVsbCAoZmFsc2UgZGlzY292ZXJ5KS4KCi0gVGhlIGxvY2FsIGZkciwgb24gdGhlIG90aGVyIGhhbmQsIGlzIHRvIGJlIGludGVycHJldGVkIGFzIGEgcmlzayBmb3IgYSBzcGVjaWZpYyBkZWNpc2lvbjogaWYgYSBudWxsIGh5cG90aGVzaXMgaXMgcmVqZWN0ZWQgYmFzZWQgb24gYSB0ZXN0IHN0YXRpc3RpYyB2YWx1ZSBvZiAkeiQsIHRoZW4gdGhlIGxvY2FsIGZkciBnaXZlcyB0aGUgcHJvYmFiaWxpdHkgb2YgdGhhdCBzaW5nbGUgZGlzY292ZXJ5IGJlaW5nIGEgZmFsc2UgZGlzY292ZXJ5LgoKLSBTaW5jZSB0aGUgbG9jYWwgZmRyIGhhcyBhIGNsZWFyIGludGVycHJldGF0aW9uIHRoYXQgYXBwbGllcyB0byBhbiBpbmRpdmlkdWFsIGh5cG90aGVzaXMgdGVzdCwgaXQgY2FuIGJlIHVzZWQgdG8gZGVjaWRlIHdoZXRoZXIgb3Igbm90IHRvIHJlamVjdCBhIG51bGwgaHlwb3RoZXNpcy4KCi0gSW4gcGFydGljdWxhciwgcmVqZWN0IGEgbnVsbCBoeXBvdGhlc2lzICRIX3swaX0kIGlmIGZkciQoeik8XGFscGhhJCwgd2hlcmUgJFxhbHBoYSQgaXMgdGhlIG5vbWluYWwgbG9jYWwgZmRyIGxldmVsIGF0IHdoaWNoIHRoZSBtdWx0aXBsZSB0ZXN0aW5nIHByb2JsZW0gbmVlZCB0byBiZSBjb250cm9sbGVkIGF0LgoKLSBUaGUgbG9jYWwgZmRyIG1ldGhvZCBjYW4gb25seSBiZSBhcHBsaWVkIGlmICRccGlfMCQgYW5kICRmJCBjYW4gYmUgZXN0aW1hdGVkIGZyb20gdGhlIGRhdGEgKHNlZSBsYXRlcikuICBUaGUgZGVuc2l0eSAkZl8wJCBjYW4gYmUgZWl0aGVyIGtub3duIChudWxsIGRpc3RyaWJ1dGlvbiBvZiB0aGUgdGVzdCBzdGF0aXN0aWMpIG9yIGl0IGNhbiBiZSBlc3RpbWF0ZWQgZnJvbSB0aGUgb2JzZXJ2ZWQgJG0kIHRlc3Qgc3RhdGlzdGljcy4KCi0tLQoKRm9yIHRoZSBzYWtlIG9mIHNpbXBsaWNpdHksIHN1cHBvc2UgdGhhdCAkSF97MGl9JCBpcyB0ZXN0ZWQgYWdhaW5zdCBhIG9uZS1zaWRlZCBhbHRlcm5hdGl2ZSBhbmQgdGhhdCAkSF97MGl9JCBpcyByZWplY3RlZCBmb3Igc21hbGwgJHokLCBpLmUuCgpcW0hfMDogeiA9IDAgXHRleHR7IHZzIH0gSF8xOiB6IDwgMFxdCgpTdXBwb3NlIHRoYXQgYWxsICRIX3swaX0kIGFyZSByZWplY3RlZCBmb3Igd2hpY2ggdGhlIG9ic2VydmVkIHRlc3Qgc3RhdGlzdGljIGlzIGF0IG1vc3QgJHokLCB0aGVuIHdlIGNhbiB3cml0ZQoKXGJlZ2lue2VxbmFycmF5fQpcdGV4dHtGRFJ9KHopCiY9JiBcdGV4dHtQfVxsZWZ0W1x0ZXh0e251bGx9IFxtaWQgXHRleHR7cmVqZWN0ZWR9XHJpZ2h0XSBcXFxcCiY9JiBcdGV4dHtQfVxsZWZ0W1x0ZXh0e251bGx9IFxtaWQgWlxsZXEgelxyaWdodF0gXFxcXAomPSYgXHRleHR7RX1fe1p9XGxlZnRce1x0ZXh0e1B9XGxlZnRbXHRleHR7bnVsbH0gXG1pZCBaXHJpZ2h0XSBcbWlkIFpcbGVxIHpccmlnaHRcfSBcXFxcCiY9JiBcdGV4dHtFfV97Wn1cbGVmdFtcdGV4dHtmZHJ9KFopIFxtaWQgWlxsZXEgelxyaWdodF0gXFxcXAomPSYgXGZyYWN7XGludF97LVxpbmZ0eX1eeiBcdGV4dHtmZHJ9KHUpIGYodSkgZHV9e1xpbnRfey1caW5mdHl9XnogZih1KSBkdX0gXFxcXAomPSYgXGZyYWN7XHBpXzBcaW50X3stXGluZnR5fV56ICBmXzAodSkgZHV9e0Yoeil9IFxcXFwKJj0mIFxmcmFje1xwaV8wIEZfMH17Rih6KX0gLgpcZW5ke2VxbmFycmF5fQoKVGhpcyBzaG93cyB0aGF0IGZkciQoeik9XGZyYWN7XHBpXzAgZl8wKHopfXtmKHopfSQgYW5kICRcdGV4dHtGRFJ9KHopPVxmcmFje1xwaV8wIEZfMCh6KX17Rih6KX0kIGhhdmUgc2ltaWxhciBleHByZXNzaW9uLiBUaGUgZm9ybWVyIGlzIGV4cHJlc3NlZCBpbiB0ZXJtcyBvZiBkZW5zaXR5IGZ1bmN0aW9ucywgYW5kIHRoZSBsYXR0ZXIgaW4gdGVybXMgb2YgdGhlIGNvcnJlc3BvbmRpbmcgY3VtdWxhdGl2ZSBkaXN0cmlidXRpb24gZnVuY3Rpb25zLgoKRnJvbSB0aGUgZXF1YWxpdHkKXFsKICBcdGV4dHtGRFJ9KHopID0gIFxmcmFje1xpbnRfey1caW5mdHl9XnogXHRleHR7ZmRyfSh1KSBmKHUpIGR1fXtcaW50X3stXGluZnR5fV56IGYodSkgZHV9ClxdCgp3ZSBsZWFybiB0aGF0IHRoZSBwcm9iYWJpbGl0eSBmb3IgYSBmYWxzZSBkaXNjb3ZlcnkgYW1vbmcgaHlwb3RoZXNlcyByZWplY3RlZCBieSB1c2luZyB0aHJlc2hvbGQgJHokLCBlcXVhbHMgdGhlIGF2ZXJhZ2Ugb2YgdGhlIGxvY2FsIGZhbHNlIGRpc2NvdmVyeSByYXRlcyBmZHIkKHUpJCBvZiB0aGUgZGlzY292ZXJpZXMgKCR1XGxlcSB6JCBoZXJlKS4KCk5vdGUsIHRoYXQgdGhlIEJILUZEUiBhZG9wdHMKCi0gJFxwaV8wPTEkLCB3aGljaCBpcyBhIGNvbnNlcnZhdGl2ZSBlc3RpbWF0ZQotIHVzZXMgdGhlIHRoZW9yZXRpY2FsIG51bGwgZm9yICRwPUZfMCh6KSQKLSB1c2VzIHRoZSBlbXBpcmljYWwgY3VtdWxhdGl2ZSBkaXN0cmlidXRpb24gZnVuY3Rpb24KICRcYmFyIEYoeikgPSBcZnJhY3tcI1ogPCB6fXttfSQgdG8gZXN0aW1hdGUgJEYoeikkLgoKQSBzaW1pbGFyIGlkZW50aXR5IGNhbiBiZSBlYXNpbHkgc2hvd24gZm9yIHR3by1zaWRlZCB0ZXN0cy4KCiMjIyAgIEVzdGltYXRpb24gb2YgZmRyJCh6KT1cZnJhY3tccGlfMCBmXzAoeil9e2Yoeil9JAoKLSAkZih6KSQgY2FuIGJlIGVzdGltYXRlZCBieSBub25wYXJhbWV0cmljIGRlbnNpdHkgZXN0aW1hdGlvbiBtZXRob2RzICgkZih6KSQgaXMgdGhlIG1hcmdpbmFsIGRpc3RyaWJ1dGlvbiBvZiB0aGUgdGVzdCBzdGF0aXN0aWNzOyBubyBrbm93bGVkZ2UgYWJvdXQgbnVsbCAvIG5vbi1udWxsIGlzIG5lZWRlZCkKCi0gJGZfMCh6KSQgaXMga25vd24gb3IgY2FuIGJlIGVzdGltYXRlZCBmcm9tIHRoZSBkYXRhCgotICAkXHBpXzAkIGNhbiBiZSBlc3RpbWF0ZWQgb25jZSAkZih6KSQgYW5kICRmXzAoeikkIGFyZSBlc3RpbWF0ZWQgZm9yIGFsbCAkeiQuCgotLS0KCiMjIyBCcmFpbnNjYW4gZXhhbXBsZQoKYGBge3J9CmxpYnJhcnkobG9jZmRyKQpsZmRyIDwtIGxvY2ZkcihkdGkkei52YWx1ZSwgbnVsbHR5cGUgPSAwKQpgYGAKCi0gSW4gdGhlIGJyYWluc2NhbiBleGFtcGxlIHRoZSB0ZXN0IHN0YXRpc3RpY3MgYXJlIHN1cHBvc2VkIHRvIGJlICROKDAsMSkkIGRpc3RyaWJ1dGVkIHVuZGVyIHRoZSBudWxsIGh5cG90aGVzaXMuIFRlc3RzIGFyZSBwZXJmb3JtZWQgdHdvLXNpZGVkLgoKLSBUaGUgYXJndW1lbnQgYG51bGx0eXBlPTBgIHNwZWNpZmllcyB0aGF0IHRoZSBudWxsIGRpc3RyaWJ1dGlvbiAoJGZfMCQpIGlzICROKDAsMSkkLgoKLSBUaGUgZGFzaGVkIGJsdWUgbGluZSBnaXZlcyAkZl8wJCBhbmQgdGhlIHNvbGlkIGdyZWVuIGxpbmUgaXMgdGhlIG5vbnBhcmFtZXRyaWMgZXN0aW1hdGUgb2YgdGhlIG1hcmdpbmFsIGRlbnNpdHkgZnVuY3Rpb24gJGYkLiBUaGUgdHdvIGRlbnNpdGllcyBkbyBub3QgY29pbmNpZGUgYW5kIGhlbmNlIHdlIG1heSBhbnRpY2lwYXRlIHRoYXQgc29tZSBvZiB0aGUgdm94ZWxzIHNob3cgZGlmZmVyZW50aWFsIGJyYWluIGFjdGl2aXR5LgoKLSBUaGUgcHVycGxlIGJhcnMgaW5kaWNhdGUgdGhlIGVzdGltYXRlZCBudW1iZXIgb2Ygbm9uLW51bGxzIChhbW9uZyB0aGUgaHlwb3RoZXNlcy92b3hlbHMgZm9yIGEgZ2l2ZW4gJHokLXZhbHVlKS4gVGhlIHBsb3RzIHNob3dzIHRoYXQgbW9yZSBub24tbnVsbHMgYXJlIGV4cGVjdGVkIGZvciB0aGUgbmVnYXRpdmUgJHokLXZhbHVlcyB0aGFuIGZvciB0aGUgcG9zaXRpdmUgJHokLXZhbHVlcyAoc2lnbiBvZiAkeiQgY29ycmVzcG9uZHMgdG8gbW9yZSBvciBsZXNzIGJyYWluIGFjdGl2aXR5IGluIG5vcm1hbCB2ZXJzdXMgZHlzbGVjdGljIGNoaWxkcmVuKS4KCiMjIyBQcm9ibGVtcz8KCk5vdGUsIGhvd2V2ZXIsIHRoYXQKCi0gd2UgdHlwaWNhbGx5IGV4cGVjdCB0aGF0IHRoZSBtYWpvcml0eSBvZiB0aGUgdGVzdCBzdGF0aXN0aWNzIGZvbGxvdyB0aGUgbnVsbCBkaXN0cmlidXRpb24uCi0gdGhhdCB0aGUgbnVsbCBkaXN0cmlidXRpb24gaW4gdGhlIHBsb3QgaXMgcmVzY2FsZWQKLSBTbywgd2Ugd291bGQgZXhwZWN0IHRoYXQgdGhlIHR3byBkaXN0cmlidXRpb25zIHRvIG92ZXJsYXkgaW4gdGhlIG1pZGRsZSBwYXJ0LgotIEhvd2V2ZXIsIHdlIG9ic2VydmUgYSBzaGlmdC4KCkluIHByYWN0aXNlIGl0IG9mdGVuIGhhcHBlbnMgdGhhdCB0aGUgdGhlb3JldGljYWwgbnVsbCBkaXN0cmlidXRpb24gaXMgbm90IHZhbGlkLgoKVGhpcyBjYW4gaGFwcGVuIGR1ZSB0bwoKMS4gRmFpbGVkIG1hdGhlbWF0aWNhbCBhc3N1bXB0aW9uczogbnVsbCBkaXN0cmlidXRpb24gaXMgaW5jb3JyZWN0CjIuIENvcnJlbGF0aW9uIGJldHdlZW4gdGhlIHNhbXBsZXMKMy4gQ29ycmVsYXRpb24gYmV0d2VlbiB0aGUgZmVhdHVyZXMKNC4gQ29uZm91bmRpbmcgdGhhdCBpcyBub3QgY29ycmVjdGVkIGZvci4KCiMjIEFkdmFudGFnZSBvZiBoYXZpbmcgYSBtYXNzaXZlIHBhcmFsbGVsIGRhdGEgc3RydWN0dXJlCgpUaGUgbWFzc2l2ZSBwYXJhbGxlbCBkYXRhIHN0cnVjdHVyZSBlbmFibGVzIHVzCgotIHRvIHNwb3QgZGV2aWF0aW9ucyBmcm9tIHRoZSB0aGVvcmV0aWNhbCBudWxsIGRpc3RyaWJ1dGlvbi4KLSB0byBlc3RpbWF0ZSB0aGUgbnVsbCBkaXN0cmlidXRpb24gYnkgdXNpbmcgYWxsIGZlYXR1cmVzLgoKRWZyb24gcmVsYXhlcyB0aGUgbG9jYWwgZmRyIG1ldGhvZCBieSBhc3N1bWluZyB0aGF0IHRoZSBudWxsIGRpc3RyaWJ1dGlvbiBpcyBhIE5vcm1hbCBkaXN0cmlidXRpb24gYnV0IHdpdGggYSBtZWFuIGFuZCB2YXJpYW5jZSB0aGF0IGNhbiBiZSBlc3RpbWF0ZWQgZW1waXJpY2FsbHkgKGJhc2VkIG9uIGFsbCB0aGUgZmVhdHVyZXMpLgoKVGhpcyBjYW4gYmUgZG9uZSBieSBzZXR0aW5nIHRoZSBhcmd1bWVudCBgbnVsbHR5cGVgIGluIHRoZSBsb2NmZHIgZnVuY3Rpb24gZXF1YWwgdG8gYG51bGx0eXBlID0gMWAsIHdoaWNoIGlzIHRoZSBkZWZhdWx0IG9yIGJlIHNldHRpbmcgYG51bGx0eXBlID0gMmAuCgpUaGUgbG9jZmRyIG1ldGhvZCB0aGVuIHVzZXMKCjEuIGBudWxsdHlwZSA9IDFgIG1heGltdW0gbGlrZWxpaG9vZCB0byBlc3RpbWF0ZSB0aGUgbnVsbCBieSBvbmx5IGNvbnNpZGVyaW5nIHRoZSBtaWRkbGUgcGFydCBpbiB0aGUgZGlzdHJpYnV0aW9uIG9mIHRoZSB0ZXN0IHN0YXRpc3RpY3MgKE1MRSkgb3IKMi4gYG51bGx0eXBlID0gMmAgYSBnZW9tZXRyaWMgbWV0aG9kIHRoYXQgcGxhY2VzIHRoZSBiZXN0IGZpdHRpbmcgbm9ybWFsIHVuZGVyIHRoZSBwZWFrIG9mIHRoZSBlc3RpbWF0ZSBvZiBmKHopLiAoQ01FKQoKIyMjIEJyYWluc2NhbiBleGFtcGxlCgpgYGB7cn0KbGZkciA8LSBsb2NmZHIoZHRpJHoudmFsdWUpCmBgYAoKVGhlIHBsb3Qgc2hvd3MgdGhhdCB0aGUgbnVsbCBkaXN0cmlidXRpb24gaXMgc2hpZnRlZCB0byBuZWdhdGl2ZSB2YWx1ZXMgYW5kIGhhcyBhIHN0YW5kYXJkIGRldmlhdGlvbiB0aGF0IHJlbWFpbnMgY2xvc2UgdG8gMS4KCi0gVGhpcyBvZnRlbiBoYXBwZW5zIGlmIHRoZXJlIGlzIGNvcnJlbGF0aW9uIGJldHdlZW4gdGhlIGZlYXR1cmVzLgoKLSBTcGF0aWFsIGNvcnJlbGF0aW9uIGNhbiBiZSBleHBlY3RlZCBpbiB0aGUgYnJhaW4sIHNvIHZveGVscyB0aGF0IGFyZSBjbG9zZSB0byBlYWNoLW90aGVyIHR5cGljYWxseSB3aWxsIGJlIGNvcnJlbGF0ZWQuCgotIFRoZSBkYXNoZWQgYmx1ZSBsaW5lIGdpdmVzICRmXzAkIGFuZCB0aGUgc29saWQgZ3JlZW4gbGluZSBpcyB0aGUgbm9ucGFyYW1ldHJpYyBlc3RpbWF0ZSBvZiB0aGUgbWFyZ2luYWwgZGVuc2l0eSBmdW5jdGlvbiAkZiQuIFRoZSB0d28gZGVuc2l0aWVzIGRvIG5vdCBjb2luY2lkZSBhbmQgaGVuY2Ugd2UgbWF5IGFudGljaXBhdGUgdGhhdCBzb21lIG9mIHRoZSB2b3hlbHMgc2hvdyBkaWZmZXJlbnRpYWwgYnJhaW4gYWN0aXZpdHkuCgotIFRoZSBwdXJwbGUgYmFycyBpbmRpY2F0ZSB0aGUgZXN0aW1hdGVkIG51bWJlciBvZiBub24tbnVsbHMgKGFtb25nIHRoZSBoeXBvdGhlc2VzL3ZveGVscyBmb3IgYSBnaXZlbiAkeiQtdmFsdWUpLiBUaGUgcGxvdHMgc2hvd3MgdGhhdCBvbmx5IG5vbi1udWxscyBmb3IgcG9zaXRpdmUgJHokLXZhbHVlcyBhcmUgZXhwZWN0ZWQgKHNpZ24gb2YgJHokIGNvcnJlc3BvbmRzIHRvIG1vcmUgb3IgbGVzcyBicmFpbiBhY3Rpdml0eSBpbiBub3JtYWwgdmVyc3VzIGR5c2xlY3RpYyBjaGlsZHJlbikuCgotLS0KCmBgYHtyfQpsZmRyIDwtIGxvY2ZkcihkdGkkei52YWx1ZSwgcGxvdD0yKQpgYGAKCi0gVGhlIHBsb3QgYXQgdGhlIGxlZnQgaXMgdGhlIHNhbWUgYXMgb24gdGhlIHByZXZpb3VzIHBhZ2UuCgotIFRoZSBwbG90IGF0IHRoZSByaWdodCBzaG93cyB0aGUgbG9jYWwgZmRyIGFzIHRoZSBibGFjayBzb2xpZCBsaW5lLiBDbG9zZSB0byAkej0wJCB0aGUgZmRyIGlzIGFib3V0IDEgKGkuZS4gaWYgdGhvc2UgaHlwb3RoZXNlcyB3b3VsZCBiZSByZWplY3RlZCwgdGhlIHByb2JhYmlsaXR5IG9mIGEgZmFsc2UgcG9zaXRpdmUgaXMgYWJvdXQgJDEwMFwlJCkuIFdoZW4gbW92aW5nIGF3YXkgZnJvbSAkej0wJCB0byBsYXJnZXIgdmFsdWVzIHRoZSBmZHIgZHJvcHMuCgotIFRoaXMgbWVhbnMgdGhhdCB3ZSBjYW4gb25seSBkaXNjb3ZlciBjb252aW5jaW5nbHkgZGlmZmVyZW50aWFsIGJyYWluIGFjdGl2aXR5IGZvciBsYXJnZSBwb3NpdGl2ZSAkeiQuIFJlamVjdGluZyBudWxsIGh5cG90aGVzZXMgd2l0aCBsYXJnZSBuZWdhdGl2ZSAkeiQgd291bGQgc3RpbGwgYmUgcmlza3k6IGxhcmdlIGNoYW5jZSBvZiBmYWxzZSBkaXNjb3ZlcnkuCgotIFRoZSByZWFzb24gY2FuIGJlIHJlYWQgZnJvbSB0aGUgZmlyc3QgZ3JhcGg6IGZvciBuZWdhdGl2ZSAkeiQgdGhlIHJhdGlvICRmXzAoeikvZih6KSQgaXMgYWxtb3N0IDEsIHdoZXJlYXMgZm9yIGxhcmdlIHBvc2l0aXZlICR6JCB0aGUgcmF0aW8gJGZfMCh6KS9mKHopJCBiZWNvbWVzIHNtYWxsLgoKLSBOb3RlLCB0aGF0IHRoZSByZXN1bHQgaXMgYXR5cGljYWxseS4gSW4gbW9zdCBhcHBsaWNhdGlvbnMgd2UgdHlwaWNhbGx5IHBpY2stdXAgYm90aCBkb3ducmVndWxhdGVkIChuZWdhdGl2ZSB6KSBhbmQgdXByZWd1bGF0ZWQgKHBvc2l0aXZlIHopIGZlYXR1cmVzLiAgCgotLS0KCmBgYHtyfQpkdGkgPC0gZHRpICU+JQogIG11dGF0ZSgKICAgIGxmZHIgPSBsZmRyJGZkciwKICAgIHpmZHIgPSAobGZkcjwwLjIpICogei52YWx1ZSkKCnBmZHIgPC0gZHRpICU+JQogIGdncGxvdCgKICAgIGFlcygKICAgICAgY29vcmQueSwKICAgICAgY29vcmQueCwKICAgICAgY29sb3I9emZkcikKICAgICkgKwogIGdlb21fcG9pbnQoKSArCiAgc2NhbGVfY29sb3VyX2dyYWRpZW50Mihsb3cgPSAiYmx1ZSIsbWlkPSJ3aGl0ZSIsaGlnaD0icmVkIikgKwogIHRyYW5zaXRpb25fbWFudWFsKGNvb3JkLnopICsKICBsYWJzKHRpdGxlID0gInRyYW5zZWN0aW9uIHogPSB7ZnJhbWV9IikgKwogIHRoZW1lX2dyZXkoKQpgYGAKCmBgYHtyIGVjaG89RkFMU0UsIG1lc3NhZ2U9RkFMU0UsIGV2YWw9a25pdHI6OmlzX2h0bWxfb3V0cHV0KCl9CmFuaW1hdGUocGZkciwgbmZyYW1lcyA9IDEwMywgZW5kX3BhdXNlID0gMykKYGBgCgpOb3RlLCB0aGF0IHRoZSBsb2NhbCBmZHIgbWV0aG9kIGFsbG93cyB1cyB0byBkZXRlY3QgZGlmZmVyZW50aWFsIGJyYWluIGFjdGl2aXR5IGluIGEgc3BlY2lmaWMgcmVnaW9uIGluIHRoZSBmcm9udCBwYXJ0IG9mIHRoZSBicmFpbiBmb3Igd2hpY2ggYSBsYXJnZXIgZnJhY3Rpb25hbCBhbmlzb3Ryb3B5IGlzIG9ic2VydmVkIG9uIGF2ZXJhZ2UgZm9yIGNoaWxkZXJlbiBoYXZpbmcgZHlzbGV4aWEuCgpXZSBjYW4gYWxzbyBlc3RpbWF0ZSB0aGUgRkRSIG9mIHRoZSBzZXQgdGhhdCB3ZSByZXR1cm4gYXMgdGhlIGF2ZXJhZ2UgbG9jYWwgZmRyIGluIHRoaXMgc2V0LiAKCmBgYHtyfQpkdGkgJT4lCiAgZmlsdGVyKGxmZHIgPCAwLjIpICU+JQogIHB1bGwobGZkcikgJT4lCiAgbWVhbgpgYGAKCiMjIFBvd2VyCgoKVGhlIGxvY2FsIGZhbHNlIGRpc2NvdmVyeSByYXRlIG1heSBhbHNvIGJlIHVzZWQgdG8gZ2V0ICoqcG93ZXIgZGlhZ25vc3RpY3MqKi4KCkdlbmVyYWwgaWRlYTogZm9yICR6JCdzIHN1cHBvcnRlZCBieSB0aGUgYWx0ZXJuYXRpdmUgaHlwb3RoZXNpcyAoaS5lLiBsYXJnZSAkZl8xKHopJCksIHdlIGhvcGUgdG8gc2VlIHNtYWxsIGZkciQoeikkLgoKVGhlICoqZXhwZWN0ZWQgZmRyKiogaXMgYW4gYXBwcm9wcmlhdGUgc3VtbWFyeSBtZWFzdXJlOgpcWwogIFx0ZXh0e0VmZHJ9ID0gXHRleHR7RX1fe2YxfVxsZWZ0W1x0ZXh0e2Zkcn0oWilccmlnaHRdID0gXGludF97LVxpbmZ0eX1eeytcaW5mdHl9IFx0ZXh0e2Zkcn0oeikgZl8xKHopIGR6LgpcXQoKV2l0aCBlc3RpbWF0ZXMgb2YgZmRyJCh6KSQgYW5kICRmXzEoeikkLCB0aGUgRWZkciBjYW4gYmUgY29tcHV0ZWQuCgpBIHNtYWxsIEVmZHIgaXMgYW4gaW5kaWNhdGlvbiBvZiBhIHBvd2VyZnVsIHN0dWR5LgoKYGBge3J9CmxmZHIgPC0gbG9jZmRyKGR0aSR6LnZhbHVlLCBwbG90ID0gMykKYGBgCgpXaXRoICRcYWxwaGEkIHRoZSBub21pbmFsIGxvY2FsIGZkciBsZXZlbCwgdGhlIHZlcnRpY2FsIGF4aXMgZ2l2ZXMKXFsKICBcdGV4dHtFfV97Zl8xfVxsZWZ0W1x0ZXh0e2Zkcn0oWik8XGFscGhhXHJpZ2h0XS4KXF0KCndoZXJlICRaJCBpcyB0aGUgdGVzdCBzdGF0aXN0aWMgZGlzdHJpYnV0ZWQgdW5kZXIgdGhlIGFsdGVybmF0aXZlIGh5cG90aGVzaXMgKCRmXzEkKS4KCi0gVGhpcyBwcm9iYWJpbGl0eSAkXHRleHR7UH1fe2ZfMX1cbGVmdFtcdGV4dHtmZHJ9KFopPFxhbHBoYVxyaWdodF0kIGlzIGEga2luZCBvZiBleHRlbnNpb24gb2YgdGhlIGRlZmluaXRpb24gb2YgdGhlIHBvd2VyIG9mIGEgdGVzdDogaXQgaXMgdGhlIHByb2JhYmlsaXR5IHRoYXQgYSBub24tbnVsbCBjYW4gYmUgZGV0ZWN0ZWQgd2hlbiB0aGUgbm9taW5hbCBsb2NhbCBmZHIgaXMgc2V0IGF0ICRcYWxwaGEkLgoKLSBUaGUgZ3JhcGggc2hvd3MsIGZvciBleGFtcGxlcywgdGhhdCB3aXRoICRcYWxwaGE9MC4yMCQgd2Ugb25seSBoYXZlICRcdGV4dHtQfV97Zl8xfVxsZWZ0W1x0ZXh0e2Zkcn0oWik8XGFscGhhXHJpZ2h0XSA9MC4yNCQsIGkuZS4gb25seSAkMjRcJSQgb2YgdGhlIG5vbi1udWxscyBhcmUgZXhwZWN0ZWQgdG8gYmUgZGlzY292ZXJlZC4KCi0gQXQgdGhlIGJvdHRvbSBvZiB0aGUgZ3JhcGggd2UgcmVhZCBFZmRyJD0wLjQ4NiQuIEhlbmNlLCB0aGUgbG9jYWwgZmRyIGZvciBhIHR5cGljYWwgbm9uLW51bGwgZmVhdHVyZSBpcyBleHBlY3RlZCB0byBiZSA0OC42JSB3aGljaCBpcyByYXRoZXIgbGFyZ2UuIFRoZSBzdHVkeSBpcyBub3Qgd2VsbCBwb3dlcmVkIQo=