3: Logistic Regression
Categorical Data
Linear to Logistic
The Sigmoid
Log Loss
Minimizing Log Loss
Multiclass Context Module Sub Header Demonstration of Logistic
Regression
Demonstration of Logistic Regression, Part I
Demonstration of Logistic Regression, Part II Context Module Sub
Header Case Study 2
Case Study 2 Introduction
Case Study 2, Part I
Case Study 2, Part II Assignment Case Study 2 Assignment
- categorical data
Hi and welcome to this video on categorical data, where we’ll we be
talking about categorical targets and categorical inputs. So categorical
data is when we have data that’s based on classes. Good examples of this
are things like colors–
is something red, green, or blue?–
or logic–
is something true or false? Other examples are things like objects.
Is it a car? Is it a house?
Is it a truck? So this data, when we see it initially–
red, green, and blue–
it’s non-numeric. But all our algorithms actually take numeric
inputs. So how are we going to take this both inputs and possibly
targets as we could be predicting whether something is going to be a red
object, a green object, or a blue object? It can both go on both sides
of the equation, both the inputs and the targets.
So how do we make this data into numeric data? The example or the
standard method is what we’re going to call one-hot encoding. So in
essence, what we’re going to do is create new indicator variables, and
those indicator variables, the object type–
or, in this case, color–
will be the column name. So we’re going to have this feature.
Basically, is it red, is it green, is it blue, or is it pink? This is
one-hot encoding and becomes essentially a vector, where the vector
represents all the different values.
So vectors can be sometimes a little bit intimidating. But what we
need to think of is we are just simply creating new variables that say,
is it red, yes or no? So as we see in the example here, the first two
lines are red. So we see a 1 in the red column.
They’re not green, not blue, and not pink, so all of those features
are 0. We can see, as we go down, each time the color appears, there’s a
1 in the appropriate column. So we see this as a vector. So the color
red becomes a vector 1, 0, 0, 0, and then blue becomes the vector 0, 0,
1, 0.
And this allows us to transform these categories or features into
numeric data. And most importantly, not only does it allow us to change
features into data, it allows us to use them as targets as well. So we
may not be used to seeing targets as vectors, but towards the end of our
video series, we’ll see how this plays out. And we can actually predict
multiple categories based on this same concept of is it red, is a green,
is it blue, is it pink?
Now, an important thing to note is only one column should have the 1
in it. All the other columns should be 0. If you do happen to find
features that are both–
that have multiple columns that are 1, make sure that that’s what you
really intend. In this case, red, green, blue, and pink are all
exclusive. But perhaps, if you wanted to break it into primary colors
and say, oh, I have red and blue and that will indicate purple, well,
then, you would have a vector 1, 0, 1. That’s extremely uncommon.
Usually, for each unique value of the feature, we add a column. And
only one of the columns will have a 1 indicated, and the rest of the
columns will be 0. But that is not a hard and fast rule. It’s more of a
guideline.
So categorical data is closely related to discrete data, but they are
not the same. And a good example of this is the number of rooms in a
house. Is it categorical? The answer is actually no.
Even though we don’t have like 3 and 1/2 rooms in a house or 3.21
rooms, what we have here is discrete data. So discrete data, it’s
important to still treat it as numeric because, in this case, the value
of 3 actually indicates something that is greater than the value of 2.
There are three rooms versus two rooms.
Now, if you have different classes–
now I’d say I have class 1 or class 2 or, in this example, category 1
or category 2–
is it really that category 2 represents twice the value of category
1? So that’s how you can tell the difference between discrete data and
categorical data. Categorical data needs to be one-hot encoded. Discrete
data, while you can one-hot encode it, can be left in its original
integer form.
So remember that when you’re looking at your possibly categorical
data to ensure that it’s not accidentally discrete data. So a couple of
examples of discrete data. We talked about the number of rooms in a
house or the number of cars a family owns. You don’t own 2 and 1/2 cars
or 4.1
cars. How many children do you have? The average family has 2.4
children. That’s not an actual number of children.
The number of children is discrete. Number of cylinders in a car
engine. Cars don’t come with a partial cylinder. They have two, four,
sometimes five for the really odd ones, but the cylinders in a car
engine is discrete.
So remember, do not one-hot encode discrete data. Allow those values
to inform your model. It’s possible you can do a data transform, but the
best way to handle this discrete data is to input it directly. So once
you’ve actually one-hot encoded your categorical features, you can use
this in fairly any model.
So if we’re trying to figure out the price of our car, and we’re
using a linear regression model, which we learned about in one of the
earlier weeks, and one of the features is the car color. So as we look
at the color of the car, we may have black cars and silver cars and gray
cars, what we’re going to do is we’re going to transform that single
column of car colors to multiple columns, where each column is a color,
and there will be a 1 to indicate, for a row, if that particular car is
that color. So I own a black car. So underneath the Black column, there
would be a 1.
And let’s say our other car colors are red and white. Under the Red
and White columns, there would be 0’s. So now I can put this into a
linear model. I can put it into any model.
Our categories have been transformed into numbers that our model can
actually understand.
- linear to logistic Welcome to our video that’s going to take us from
linear regression to logistic regression. So we’ve encountered
categorical data now, and the question becomes, what happens if the
target or our prediction is actually one-hot encoded? So the idea being,
let’s try and predict if something is true or something is false. And we
saw, in the past, that we one-hot encode these, and that can transform
our categorical data into numeric data.
But, now, what happens if our target is now this category? We’ve
one-hot encoded the target, but, remember, linear regression doesn’t
predict a zero or a one. Linear regression produces a continuous output.
So even if we manage to get that output between zero and one, we would
have a value that could be anywhere from zero to one, and, quite
possibly, could go over or under.
So the question becomes, how can we use what we’ve learned in linear
regression to tackle these problems where the output is–
or the prediction–
is categorical? So when we look at what happens with a continuous
output versus a categorical output, what we need to do is, we will see
on the left hand side that the output varies with x. So there’s a
continuous output. The discrete output means that y can only take on
specific values.
In this case, it would be roughly negative one or one. And it doesn’t
have to jump exactly at zero. In this case, it can take on different
discrete values for different ranges of x. It doesn’t have to be x
greater than 0, x less than zero.
It could be something like x greater than negative one or x less than
than negative one. But we see that y only takes on discrete values. So
we talked about discrete numbers, but this is now a discrete target.
And, in particular, we’re going to look at binary targets where we’ve
got two values.
One of which will be a value zero, and one of which will be a value
one, which will correspond to our one-hot encoded targets. So to do
that, we’re going to come back to linear regression. And, hopefully,
these mathematical equations look familiar to you, but it’s our old y
equals mx plus b. So, of course, each column, x–
in this case x1, x2, et cetera–
has an associated slope, m1, m2. And a reminder that the intercept is
hiding as m zero. So if you’re wondering, why is this the sum of mi xi,
x0 is actually one, and m0, of course, will then be the intercept. And
this is the compact notation I mentioned a few videos ago.
So what we’re going to do is, we’re going to take a variable
transform. And this variable transform is going to take us to logistic
regression. Now, it seems like this is a strange type of variable
transform. But, as we’re going to see, this actually forces our output
to now essentially be discrete.
We’re going to model a discrete output, and that’s what this function
is giving us. And this function will lead us to logistic regression.
Now, one of the things you’ll notice is I add this sigma at the bottom,
sigma of mi xi. And that simply means that the sigma function, or what
we’re going to call the sigmoid function, takes as its input, m times
x–
the summary of that. And, typically, we’ll drop the summation. It’s
implied because we know for linear regression that we see this original
top equation–
the m1 x1 plus m2 x2, et cetera, et cetera. Because we’re so familiar
with that, for shortness and brevity we typically drop the summation
sign. But it’s already there. And so I’ve dropped it here for clarity,
but you can see that y is now a function of m times x rather than m
times x directly.
- the sigmoid
Hi, and welcome to the video about the sigmoid function, which is one
of the key functions in transforming from a linear regression to a
logistic regression. So in the last video, we talked a little bit about
this variable transform. Now, we can start to see what this variable
transform actually does to our data. So this sigmoid function takes the
input x, and it essentially squeezes the output so that it’s between 0
and 1.
And it gives us the S-shaped curve that we see on the left. However,
as that slope–
and when I say slope, this would be the slopes m–
get larger and larger, we see that this sigmoid function becomes much
more like a step function, modeling the behavior that we saw for
discrete output. And this shows us that we can actually get an output
that is essentially forced to be either 0 or 1. Now, we’ll still get
outputs that are between 0 and 1. So some people may say, well, although
we’ve squeezed the tails to 0 and 1, what about the point in the
middle?
And what we’ll look at, at these points in the middle, is these are
going to represent probabilities that we belong to class 1 or class 0.
So what we’re looking at now is we will find a threshold or a cutoff in
our sigmoid function. And we’ll say anything above this value is class
1. And anything below it is class 0.
The default, of course, would be at y equals 0.5, the idea being
essentially rounding. So we could take–
if its 0.51 and above, it belongs to class 1. And if it belongs to
0.50 and below, it belongs to class 0 like our normal rounding would
be.
However, this is not required. You can make your thresholds wherever
you want. And this is important as we look forward. And we maybe want to
be cautious about certain assignments.
So what if an assignment for 1 is a fraud detection? Well, we might
want to have either a very low threshold. Like we want to detect any
possible chance of fraud. So perhaps when our y output value is say
0.2,
so that would mean that there’s about a 20% chance that there’s fraud
going on. So maybe we should check that. On the other hand, if you put
the threshold really high, you would say, ah, I’m extremely certain that
fraud is going on at something like 0.99. But the point is you have the
choice to adjust that threshold.
So the default output if you don’t instruct your algorithm will make
the cutoff at 0.5. This is one of the most common errors I see students
make is they don’t question where that threshold should be. Now, there
are a number of ways that we can determine that threshold. It’s not
entirely a judgment call.
But be aware that you can make the determination where should your
assignment be for class 1 and class 0. So let’s take a look at the
properties of the sigmoid function. We can see that we have the output y
is 1 over 1 plus an exponential. And that exponential, again, the
implied sum, we have here mx.
But it’s actually a summary of all m’s and all the x’s. So, again, m0
times x0, x0 being 1, m0 being the intercept, x1 m1, our first column
and first slope. But the idea is if that sum total is extremely large
and it’s positive, we’ll have 1 over 1 plus e to the negative large
number. e to a negative large number is, in essence, a very, very small
number because it’s 1 over e to that power.
So that number becomes 1 plus an extremely small number. That becomes
1 over 1, which means our output is 1. If our m times x is negative–
now, remember, we have a negative sign in front of m times x. That
means m times x itself is negative. Then we have an e to the positive
number because the two negative signs will cancel out. Now, I’ve got an
exponential to a very large power.
Well, anytime I take something to a large power, that number gets
very, very large. So I have 1 over 1 plus a large number. 1 plus a large
number is essentially that large number. And 1 over a large number is 0
roughly.
Now, again, I like to talk in physics terms because of my physics
background. But the idea is that we are extremely close to 0 with 1 over
a large number. And that’s the behavior we actually want. We want if the
output is large to be, yes, this is class 1.
And that’s how we’re going to model it. You may ask yourself, why did
we say if mx is large, we want it to be one, and if an x is negative, we
want it to be 0? Well, that’s the idea of us taking advantage of this
sigmoid problem. So we will frame the problem such that we want the
output to be large for class 1, and large and positive, and negative for
class 0.
This simulates the output of the step function. And as an added
bonus, although many people are intimidated by the mathematics of
exponentials, exponentials actually have some very friendly properties
that we’re going to take advantage of. And they’ll help us with doing
some advanced mathematics in the next couple of steps. So we’ll use this
sigmoid function to give us an advantage.
And, hopefully, we’ll see that in the next couple of videos. And it
won’t be so obscure why did we choose this particular function. At its
face value, we’re looking at, of course, outputs that are 0 and 1. And
this function starts to simulate that.
And then we’re going to take advantage of that exponential property
as we go forward.
- log loss Hi, and welcome to our video on logarithmic loss. So, in
linear regression, what we find is that we typically use the mean
squared error or the mean absolute error. And the idea is, we are
measuring the distance between our prediction and the target. And
because distance, of course, is Pythagorean theorem based, we typically
use the square.
However, because we’re also one dimensional with prediction and
target, it’s also possible to use the mean absolute error. And all we’re
doing is just measuring how far away from the actual value is our guess.
And so our loss is actually the sum of the distances from the prediction
in the value of x to the target in y. And that’s why we use the vertical
distance rather than the shortest distance.
The idea is that the input and our data is used and fixed for us. And
so we adjust the slope of the line to provide a prediction. And the
distance from the position at input x to the target y is then our
vertical distance. So our object is to get our straight line as close to
the target points as possible, minimizing all of those distance.
That’s what we’re doing when we’re using both mean squared error and
mean absolute error. However, when we get to logarithmic loss and
categorical data, we’ve got a problem. Here, our output or our target,
our guess, really has only two values. Those values tend to be zero and
one.
So what we need is, we need a loss function that represents our
distance to this target and our error that when we guess, is it target
zero or is it target one, how far away from that guess are we? So we’re
actually going to have two cases because we have the case where our
target is zero and the case where our target is one. So if the target is
one, the error is just going to be minus y log with p. And p is just our
output of our sigmoid function.
I’ve mentioned, if we’ve been dealing with sigmoid functions, we know
that the output of the sigmoid function represents a probability that
that input belongs to a certain class. So p is really just the output or
the sigmoid of m times x. It’s that one over the exponential. So what
we’re doing is, we’re looking at the distance between our target and our
guess or our output.
And we put y in front of it because we’re going to combine these two
cases. Now, in this case, the target y is one, so our error is really
just minus log p. Now, here, I’m using the traditional notation used in
data science. In this case, the log is not log base 10, it’s actually
the natural log.
That will become important in a few moments. Now, when the target is
zero, we see we have a one minus y. In other words, one minus zero times
the log of one minus the probability. This is basically an
exclusion.
So, , just like before, we said, we had a probability to belong to
class one. Well, if we’ve got a class one and a class zero, and we look
at this as, what’s the probability I belong to class one? But they’re
mutually exclusive. So that means that the probability I belong to class
zero is one minus the probability of belonging to class one.
It’s simply finding the probability that I belong to the other class.
But these two classes are coupled, and so we see the one minus p. So,
again, p is our prediction or output of our sigmoid. So if we put these
two terms together, we get a two-term loss function.
But you see that one of those terms is always zero. If y is one, the
right-hand term becomes one minus one or zero, so that term contributes
nothing to the loss. On the other hand, if y is zero, the left-hand term
becomes zero. And we have, once again, minus log of one minus p.
And that’s the idea of, what’s my probability of belonging to class
zero? So both terms are symmetric. What we’re seeing is we are just
taking advantage of our classes being coupled to select which term we’re
going to use. So, here, what we’re doing is, we are penalizing
predictions that are farther away.
So if I’m trying to predict zero and my output is really, really
large, log of one minus the probability is going to be a high
contribution to the loss. On the other hand, if my prediction–
I want it to be close to one–
my target is one, then the output should be close to one. And that
means my probability should be close to one. As the farther and farther
I get away from one–
and, remember, the only way to get away from one is to move towards
zero. By using that minus log p term, I’m actually increasing the value
of my error. So the more I predict zero–
or the closer I am to predicting zero, the more I contribute to the
loss. In other words, my error is larger. So, sometimes, it helps us to
take a look at this. And let’s take a look at that visually now.
So, here, we can see what log loss looks like, and we have the
individual terms. We can see the blue line, which is the probability for
y equals one–
or I should say the loss for y equals one–
you’ll see, if we’re predicting close to one, our loss is very, very
small. But if we’re predicting close to zero and our target is one, we
contribute a great amount to the loss. And the idea is, when we’re
trying to minimize this loss equation, what we’re going to try and do
is, force our predictions to be correct. In other words, the model
should start outputting predictions that are closer to one because they
will contribute less to the loss.
Likewise, when we’ve got a target that’s zero, the farther away from
zero you are, the more you contribute to the loss. Now, remember, our
sigmoid is going to squash our outputs to be between zero and one. So
the farther away you are from zero, means the closer you are to one. If
we add these two terms together, we see that it forms a concave
curve–
or I should say, a convex curve–
with a well-defined minimum. And this is what we always wanted for
any of our loss minimizations. We want a well defined minimum. Well, we
see that this logarithmic loss has that well defined minimum, and it’s a
function of these two terms.
So by using these two terms with the values of y equals one and y
equals zero, we formed this loss function with a well defined minimum.
That gives us an advantage because a well defined a minimum for us is
easy to find and minimize.
- minimizing log loss
Hi, and welcome to the video on minimizing log loss. So, once we have
a log loss equation, our question becomes, how do we minimize that? And
of course, minimizing our log loss, means that our model is producing
good predictions. So a small loss, or a minimal loss, gives us what we
want–
a good, well-fit model. We’ve discussed the loss function. We have a
prediction function. So if we take the sigmoid prediction function and
our logarithmic loss, let’s try and combine them in a similar fashion to
linear regression.
And keep in mind that although we see the value p, p is really a
function of x. And although I shouldn’t say function of x, p contains
the data, x, and is actually a function of the slopes times x. So we’ve
got a little bit of math here. But it’s important for us to understand
how this works, and how it’s similar to linear regression.
So traditionally, we denote the loss with the value J. And here, what
I’m doing is I’m summing up all the loss terms for each prediction. So
again, if the prediction is 1, we’re going to use the left hand turn. If
the prediction is 0, we’re going to use the right hand term.
Or I shouldn’t say the prediction, if the target. And as we sum all
of those up, we will get the final loss. And just a quick reminder that
I have p in the loss equation. But remember that p is actually the
sigmoid that contains our data and our slopes.
And remember, that’s the same as linear regression, that sum of mi
xi. So, we’ve got things that are fixed. Our target, y, is fixed. That’s
our target.
It’s our data. Our x values are fixed. That’s our data. So to make a
minimum value of J, the only thing that can change is the slope.
And because we have this function that has a well-defined minimum, if
we take the slope of the function J, what we can do is we can find the
minimum. Because what we’ll do by looking at the slope is we’ll find,
which way does the direction of the minimum lie? Does it lie to our
left? Does it lie to our right in each dimension?
And so by taking the partial derivative of J with respect to the
slope, we will get an update rule for the value of the slope. Now
conveniently, the update rule for logistic regression is the exact, down
to the variable function as the linear regression. In other words, I
have this sigmoid function. And I have an update rule.
And when I take the actual derivative of the update rule, it turns
out that the value is the same. Now, although it’s a nicety to see, I’ve
included a link here so that you can go through the details of the
derivation yourself. But the key takeaway from all this mathematics is
our update is the same. And if our update is the same, that means that
we can use all the algorithms from linear regression to update our
values of the slope for logistic regression.
So now we’ve extended our ability to model problems from linear
problems to categorical problems, simply by using an appropriate loss
function. So now, hopefully it starts to become a little bit clearer why
we use this particular exponential function. It was actually chosen so
that those update rules come out the same. And again, just a quick
reminder, although I’ve got log, L-O-G, these are actually natural
logs.
And that can be important if you’re taking those derivatives, because
the natural log derivative is actually 1 over the value. So the
derivative of natural log of p is 1 over p. That plays an important role
in this update rule coming out the same.
- multiclass
Hi, and welcome to the video on multiclass classification. So the
question may come up to your mind. We’ve been studying how to do binary
classifications. And our sigmoid function gives us a binary output–
class 1 or class 0. So you may come across the log loss as binary
cross entropy loss. But as we extend to more and more complex problems,
not every problem is a simple example of two classes. We could have
three classes, 10 classes, 1,000 classes, or even a million classes.
So how can we get from our binary classification, the output of the
sigmoid being 0 or 1, to multiple classes? And a simple example of this
is the iris data set that’s a classic toy data set. There are three
species of flower within the iris. And the question would become, how
would I do a logistic regression on that?
Because a sigmoid output will only be 0 to 1, but I’ve somehow got
three classes. And even if I did a one-hot including, my target is no
longer 1 or 0, there’s actually three classes that have ones and zeros.
So how would I adapt my logistic regression to deal with these multiple
classes? So the first method that we can use is something that’s called
one-versus-all or one-versus-the-rest.
So what we’re going to do is for each class, we will train a
discriminator or a model. So if I have three classes that correspond to
the colors red, blue, and green, my classifications would be red/not
red, green/not green, blue/not blue. So I’ve got a classifier for each
class. And what we’re going to do is we’ll find which class gets the
highest output value.
What happens is you’ll look at the sigmoid, and although we often
talk about the sigmoid outputting a 0 and 1, in reality, we get some
value between 0 and 1. And so to pick the winning class, what we will do
is find out which output is the largest. Now we frame these problems as
when we do the red/not red, red would be class 1, and not red would be
class 0. That way the not value is always class 0, and the value is
class 1.
So we will come out with three values. And what we will do is we will
look at those three values, and they may all be very large and very
positive. The key is, which one is the largest? So if we look and say
red has a score of 0.97
and green has a score of 0.22 and blue has a score of 0.50, well,
because red has the highest score, that’s the winning model. So we’re
trying to apply our models off against each other.
It’s not really a competition, but we’re looking at each of the
output scores and finding which one gives us the max score. And
oftentimes, we’ll see a function called argmax, which means it will look
at the outputs of all three values and just takes the maximum value and
look for which one is maximum, and it returns that class. That’s the
one-versus-rest or one-versus-all method. The downside, of course, is
you have to keep a model for each of your classes.
So this isn’t so bad if we’ve got three classes. But if we had a lot
of classes–
100, or 1,000, or even that 100,000, million classes–
that could be quite time consuming. So that is one of the drawbacks
of logistic regression. It’s not inherently multiclass, but we can adapt
to it. But as the number of classes increases, it becomes more and more
difficult for us to find a solution that’s easy for us to implement.
It’s hard to manage millions of models. It’s not so bad to manage
three models. So here is an example of some decent outputs of the red,
blue, and green. So we look at the red score, 0.87,
the blue score, 0.22, the green score, 0.45. So we would look at
those and form a vector of the positive outputs. And when I say the
positive outputs, the class 1 outputs.
So here I have 0.87, 0.22, and 0.45. So with 0.87 being the largest
value corresponding to the red value, once again, we’re going to
classify that as red.
So one of the issues is if your data is not evenly distributed, what
our classifiers are going to see is a large number of negative examples.
And because classifiers learn based on probability, they may start to
favor the not example. So let’s say our data is evenly distributed, with
1/3 red, 1/3 blue, and 1/3 green. Well when I go to the red/not red
classifier, 1/3 of the data is red, but 2/3 of the data is not red.
And this can have the effect of biasing our models towards the 0
class probability. So keep that in mind, especially as the number of
classes increases. This would get worse if I had 10 classes, 10 colors.
Then 10% of the data would be red and 90% of the data would be not
red.
So as a basic guess to get 90% accuracy, you could actually predict
100% of the data being not red. That’s an example of biasing the data.
Now in reality, the classifier will hopefully pick up which input
variables contribute to the class red. But be aware that as you get more
and more classes, your classifiers will start to get biased for negative
samples.
And there are a number of things that we can do to take care of that
bias, but you have to be aware and fundamentally address them as you get
to larger and larger multiclass. Now, there’s another way to handle this
multiclass, and it’s more of a head-to-head battle of classifiers. And
again, we use these which one’s the max, which one’s the best–
a head-to-head battle. But it’s setting up a comparison. So we’re
going to do a comparison for all these classifiers. The problem is this
can get out of hand quite quickly as we can see the number of
classifiers starts to expand quite quickly.
So here, we’re going to be even worse as we go to multiple classes.
And what we’re going to do is compare things like, is it red? Is it
green? Is it green?
Is it blue? As we go through that sequence, obviously we can start to
see there’s factorials involved. But that’s why we see the number of
classifiers expanding quite a bit more than we had for the
one-versus-all. Whereas if we had 10 classes, we would have 10
classifiers.
But this also allows direct comparisons between the different
classes. And the idea here is the class that wins the most votes is the
assigned class. So for the example of red and green and blue, we would
have a red/green and a red/blue. So red has two opportunities to
win.
Well, just like that, if you’ve got the red/green, you’ve also got
green and blue. So green has two chances to win, and so does blue.
That’s why you see as the number of classes expands, the more
head-to-heads we tend to get. And this can lead to some crazy solutions
where some of the classes aren’t super popular, but it’s very difficult
for our classifiers to distinguish between different parts.
So one-versus-one, although it is used in smaller classifiers, it’s
typically not used as we go to larger and larger numbers of classes. But
the one-versus-one is out there, and in many of our algorithms is
implemented behind the scenes, so you don’t have to write this from
scratch. You can say, I want to do a one-to-one classifier. Here are my
inputs, here are my targets, and the algorithm will set up all these
classifiers for you.
So here I’ve got a four-value example of Texas, Iowa, California, and
Florida. Now it turns out I need six head-to-head classifiers. So
because SMU is in Texas, I purposely set this up so that Texas would
win. And we don’t actually know what these values are, but this is just
an example.
And so I’ve got the Texas-Florida classifier. And you can see that
Texas score is the largest value there. So Texas is, quote unquote, “the
winner” of that classifier. And we have the Texas versus Iowa.
And you can see once again, Texas comes out on top. Well then we’ve
got the Texas versus California. And we see once again, Texas is
triumphant. All of our students in Texas are celebrating right at this
point.
Now we go to the California versus Iowa. Before we were looking at
Texas, all the Texas head-to-heads. Now let’s look at the California
head-to-heads. So California has already gone head-to-head with
Texas.
Now it needs to go head-to-head with Iowa and Florida. And you can
see it comes out on the losing end versus Iowa. So Iowa wins that
head-to-head battle. And California comes out on the bottom against
Florida.
So Florida has won one. So our score now is Texas 3, Iowa 1, Florida
1. So we’ve got left is the Florida-Iowa battle. And of course, the
classifier for this result says Iowa comes out on top.
So our final tally is Texas got three votes, Iowa got two votes,
Florida got one vote, and California has zero. And we call these votes,
but we’re just tallying the winner of each classifier. So we can sum
these up and we’ll get these results. But what can happen is a state, or
in this case, a classifier can actually lose the head-to-head and win
the overall classifier–
which can seem strange. So remember, because this is a summary of
outputs at each head-to-head, you can get different results. So one of
the other problems is you can end up with ties, where everybody ends up
in a tie. It’s rare, but it can happen.
So here we’ve got three votes–
or two votes for three states and six classifiers. So we have a tie.
There’s no way to break that tie statistically in how this is set up. So
we have two methods to attempt multiclass.
Neither one is perfect. Remember that the one-versus-one can have
these ties. And the one-versus-rest or the one-versus-all can suffer
from unbalanced classifiers. Again, we’ve got that idea of class/not the
class.
And as the number of classes increases, we have this bias towards the
negative class, just because of the number of samples that the classes
are going to see. So both of these implement what I like to call “under
the hood.” We’re not going to set these up individually. What we’ll do
is we’ll go to sklearn and we’ll say I would like a one-v-one model
situation.
Here’s my data–
run. Or I have a one-v-r. I have a one-versus-all method multiclass.
Here’s my data–
set it up. So it takes place behind the scenes that it’s
well-established how these are run. And so you get your final outputs.
So once again, it allows us to focus on what’s going on with the problem
rather than implementing the software solution.
The software solution is there. It’s for us to use the appropriate
software correctly, but it’s already been optimized so we can get the
best performance and less worrying about the specifics of the coding and
more about the problem itself.
- demonstrating of logistic regressio, part 1
Welcome to our demonstration of logistic regression. So to get
started, we’ll do our traditional imports. And now what we want to do is
we want to find a binary classification set. To practice, Sklearn has a
number of data sets.
You can always scroll through them. And I’ve chosen one to start with
that gets us started with our binary classification. So we’re going to
be taking a look at the breast cancer data set. Let’s go ahead and do
our first tab.
As always, let’s take a look at our data. So you can see this is not
actually a data frame. What we’ve got here is actually a dictionary,
where we’ve got our data and we’ve got our targets. And then we’ve got
all the information we need.
We just have to put it into a data frame. That’s not too hard. And
this is the traditional way that data sets are stored in Sklearn. So if
we’re not familiar with dictionaries, we’re about to get a short
lesson.
So you can see this is our actual array of data. So let’s start to
create a data frame. And I’ll just call it cancer data frame. And for
brevity, this is the data that’s going to go in.
So we’ve got our indexes. Now you’ll notice we don’t have any column
names. But the column names were actually there in the raw data. Let’s
take a quick look.
So we’ll have to remember to add our targets here. But here’s the
description of all the different names. So let’s see if we can pull up
those descriptions. So these are all the descriptions.
And let’s see if we can pull out the names for these really quickly.
So this is going to take us a little bit. So I may cut the video at this
point while I come up with a quick way to put these together. We can see
that it’s not exactly easy to pull these out and would take a little bit
of formatting.
It’s not impossible. But for the meantime, I’m just going to move on
without having my actual names. We know this data set is well formatted
because it’s a toy data set as well as you can read the summary on
Sklearn. So we’ll just continue.
But we don’t need to forget that we actually have to add the target
as well. So let’s go ahead and add that to our data frame. So let’s look
at our final data. Oops.
This is why I tab complete quite a bit. So we can see we’ve got our
values. And if we wanted to look up what those values are, we could.
Then we see we’ve got targets of 0’a and 1’s.
So we’ve got our data. It’s binary classified. What we need to do is
let’s start our basic logistic regression. Now, you’ll notice at this
point, I have not scaled the data.
But we can still get fairly good results without scaling the data.
And by not scaling the data initially, we can perhaps compare and
contrast the importance of scaling the data. Fortunately, doing our
logistic regression is actually going to be fairly quick. Now, this is
the first time we’ve encountered our Sklearn.
You can notice that there are multiple versions of logistic
regression. Now we always like to do cross-validation. But you can use
regular logistic regression and a cross-validation from other parts of
Sklearn. The Logistic Regression CV, because it’s such good practice,
has it built in.
At this point, let’s go ahead and take a look at our Logistic
Regression CV. So I’ve imported this class. And we’re going to use this
as our model. Now, a little bit of object-oriented programming, Logistic
Regression CV is a class.
So we’ve just imported this class. And now what we need to do is
we’re going to create a model. So our model is an instance of the
Logistic Regression class. Now, we can accept the default parameters by
just using empty parentheses.
If you’re interested in all the options, you may want to take a look
at the Sklearn documentation. So we may get some mornings as we start
this. But this is our base logistic regression cross-validation. Let’s
go ahead and instantiate it.
So now we’ve got a model, now it’s time for us to fit our model. So
to fit our model, we need both targets and data. But that was in the
data frame. We’ve already got that.
So I’ve got my data frame. But remember, the data frame contains both
the targets and the data. So we need a way to split this up. I can do
that with pandas location.
Or I can actually split these up. And either way, we will get,
essentially, our x data and our y data. So what I can do for the y data,
we know that that’s the target. Now, this may look a little crazy.
But what I’m doing is I’m creating an object where I drop the target
column. And then here are my targets–
the actual target column. You may be afraid that when I drop the
target, I’m going to drop it from the data frame completely. However, in
this case, because I’m not assigning the result to anything, this just
returns an object. It has no name.
So this is essentially our x data, while this is our y data. We’ll
probably get a warning because we didn’t specify how many folds in our
cross-validation. But let’s go ahead and see if this works. Well, we
found not in the axis.
So I probably misspelled this. And let’s see if we can figure
out–
hmm, this is interesting. So this is one of my most common mistakes.
I forget to tell it if I’m dropping a column or a row. OK, so we’ve got
some warnings here.
And this is our results. And while these are warnings, they’re not
too terribly of concern with us. And it’s telling us we’ve reached our
iteration limits. In other words, its iteration.
But it may not have finally found a solution. So we finally got our
done. And we’ve got our results. So we don’t have anything printed.
How do we see our results? Well, they’re actually stored as part of
the model. Now, you’ll notice I actually put–
I did that quite quickly. Let me stop and go back. Because they’re
stored as part of the model, this model is an object and it has
attributes with it. Those attributes can be accessed through various
methods.
Again, we’re into a little bit of object-oriented programming. But
the key thing is we can access those attributes. When I hit the period
here, this means I want one of the attributes or methods of my model.
What I can do is now I’m going to hit Tab.
And this will bring up a list of attributes. So this will tell us all
the different things we want to know. First of all, the C, that’s going
to be our regularization. We may or may not have talked about
regularization at this point.
But an important reminder that regularization is key to any model.
One of the most important things is, of course, our coefficients or our
slopes. So let’s take a look at what our coefficients are. This is going
to tell us all the coefficients or slopes for each columns.
Now, at this point, our columns are unnamed. But we can actually tie
these back because these are in order. This is the coefficient for the
first, or zeroth, column, the second column, and so on and so forth.
Obviously, the larger in absolute value the column, the more important
it is.
But if you remember, we didn’t normalize that data. So the range of
the data can have an impact on these coefficients. So the importance is
somewhat hidden. And we’ll see that when we normalize our data in just a
moment.
But we’ve got our coefficients out. Let’s see if there’s anything
else. Let’s take a look. Is the slope part of these coefficients or
not?
One of the quick ways we can look at that is let’s take a look at our
data frame. And remember, we’ve got an extra column in there. That’s the
target. So we see that there’s 31 features.
Let’s take a look at how many coefficients there are. So you’ll
notice there are 30 coefficients. Now, this shape was 31. But don’t
forget we’ve got the targets in there.
So we’ve got a coefficient for each column, which means the
y-intercept is not actually included in these coefficients. It’s always
good to check to see whether they consider the intercept independent of
the coefficients. Because many times, we group it in. And because data
science is conglomerate and brings in methods from multiple sciences,
you can never be sure.
So it’s always good to check whether or not your assumptions are
correct. I would have initially assumed that the intercept was part of
these coefficients. But in this case, it’s not. Let’s take a look at
some of the other things that are involved here.
So as we saw, we’ve got the different values. We see here is the
intercept. And we’ve got all the other parameters. What we didn’t
do–
my apologies for accidentally clicking on that–
was we didn’t know how many cross-validations we did. So let’s see if
we can find that out now. And it’s actually not telling us. So we can’t
understand at this point what was the default cross-validation.
This is sometimes when we might have to go to the documentation. But
let’s see if we can possibly change that because we know this is a value
in our logistic regression. If we remember our fit method or possibly
our instantiation, we can possibly change the value for CV. So let’s go
back.
And I’m going to create a new model. And, of course, it’s an instance
of the Logistic Regression CV. And let’s see if we can figure out how to
change the number of folds. Now, we’re not seeing anything.
I press Tab to come up. And so it’s not showing us. One of the things
I can do is–
I know that typically one of the things is n folds. Now, I’ve got n
jobs. And at this point, because I don’t use logistic regression and not
a lot, I may have to go back and refresh my memory with the
documentation. So let’s probably go and do that.
And let’s take a look at the Sklearn documentation. It’s a little bit
hard to remember all the things. So don’t be ashamed if you can’t
remember these things off the top of your head. So let’s create a new
tab.
And I’m going to go and open up the Sklearn documentation.
- Demonstration of Logistic Regression, Part II
Here we are at the SK Learn documentation. What we’re going to do is
we’re going to look for that logistic regression CV. Now there are a
number of ways to get there. We’ll just type that in.
And we see logistic regression CV pop up. Now we’ve got our
documentation. And we can see right here how to define the different
important parameters. And we see that we may have caught ourselves a
little bit because we see the default is CV equals none.
And so we have this CV. And we look down here. It’s the
cross-validation generator. And the default being none.
So even though we started off assuming we were doing
cross-validation, it turned out we weren’t doing any because we didn’t
have an integer. So to properly do a cross-validation, we’ll actually
have to assign an integer. This is why it’s helpful to always bring up
the documentation. It’s fairly clear here in SK Learn.
And it’s one of the benefits of this package to go through. So while
we’re here, we can take a look at all the different options just to
remind ourselves that we’re doing things the correct way. So we start
with our cross-validation. And as we browse through this, we see that
this would be our regularization, our Cs, as I may have mentioned
before.
That may not be obvious. I’ve been here before so I know that C
represents the regularization. We’ll see our penalties down here. We’ll
talk about that frequently throughout the course.
We’ll talk about our scoring. So we can have a report on the
different scoring. And right now it’s not telling us any scoring. We saw
the stopping criteria.
Now you may ask yourself, what do you mean stopping criteria? As the
different solvers are used, they stop when the loss begins to decrease
or stops decreasing a certain amount. And so we don’t typically use
this, but that’s with our warnings were about if you may have noticed
and we’re reading along. We have various issues for class weights and
efficiencies.
These are more computation. So our main thing is let’s go back and do
our cross-validation. So I’m going to click back. And let’s do a
five-fold cross-validation.
So we’ve got this new model. And let’s go ahead and fit it. Now I’m
going to go and scroll up. And I’m just going to copy what I used
before, because I know it works.
So we’re seeing still we’re getting the number of iterations. But now
we see we’re still not getting results. Let’s see if we can add in that
scoring metric. So we’re just going to rewrite, even though this is our
new model.
And let’s see what the accuracy is. So once again, we’re just going
to use the same fit command. And we haven’t had anything come out. So is
that stored somewhere within our model?
So let’s take a look. Aha. We’ve got scores in here. So what we’re
seeing on scores is not what we expect.
We saw for scoring we expected actually five values out. So this is
something that’s not our actual scores of our cross-validation. So when
we actually do the score, this is asking us for our x and y values. What
we’re finding is that the CV method does not actually return to us the
scores for our cross-folds.
So what we can do is we can actually move back either to a regular
logistic regression and use the model selection to do cross-validation.
However, at this point, let’s see what our score is just for our basic x
and y. I’m just going to quickly pull these. And you can see we’ve got
97% accuracy.
However, the astute among you will notice this is data that was used
to fit the model. So this is a biased estimate. And what we’re really
interested in is our unbiased estimate. It doesn’t appear that the
logistic regression CV is immediately forthcoming with those unbiased
estimates.
I’m going to take one more quick look before we move on to a
different method. From this perspective, I’m going to move on to the
cross-fold validation in the model selection to see if we can get a
better estimate of our scoring. To do that, I will import the
cross-validation method. And we’re going to switch to regular logistic
regression.
What I’ve done here is I’ve imported a method called cross_val_score.
This will give us the scores across a cross-validation for any model. So
this is not unique to logistic regression like the logistic regression
CV was. So let’s go ahead and use this cross_val_score with our log reg
model, which is a simple logistic model.
So what we’ll need to do is we’ll need to understand how to do the
cross_val_score. For that, let’s take a quick look at SK Learn. We see
here cross_val_score takes an estimator. That’s going to be our log rog
model, our x and y data.
It’s going to take our scoring. So that will be what metric we want
to use. And then it will use a default five-fold cross-validation. Now
you’ll notice here it says none.
However, if we read in the details, none uses the default five-fold
cross-validation. This is probably the origin of why I myself tend to
use five-fold cross-validation so that we know if we call this with the
default parameters, we’re getting a five-fold cross validation. So let’s
go back and do that. Now we need our estimator.
We need our x data and our y data. We remember that we don’t need to
specify a CV because the default is 5. Let’s add our accuracy in. This
line has gotten a little long, but you can see I’ve got my
cross-validation score.
I put in what model I’m going to use. I have my x data. I have my y
or target data. And I have the scoring I want to use.
Let’s go ahead and run it. And you can see here we’ve got a slightly
lower value. This is important. It’s not bad.
We didn’t do anything incorrectly. What these represent are the
unbiased estimates. Before our 97% was a biased estimate. We used the
entire data to fit that particular model.
However, when we ran the data through, that data had been used to
build the model. In other words, the model had seen the data ahead of
time, resulting in biased results. Here, of course, we split our data.
And so when data was sent through for scoring, that data was not used to
build the model.
And of course, the reason we have five scores with five different
values is we split the data into five parts. And each time that part of
the data was held out as a test set and the other parts were used to
train the model and fit it. The test set was then run through without
the model having seen it before and thus produces an unbiased
prediction. So we see that while these predictions are a lower score,
they’re actually a more accurate–
with no relation to our scoring using accuracy, they’re a more
accurate representation of how our model will perform on unknown data.
This is a good example of how logistic regression and cross-validation
can be used together to get that estimate of performance on an unknown
data set. At this point, we’re going to go ahead and conclude our demo
on logistic regression.
- Case Study 2 Introduction Welcome to our case study on diabetes.
This week’s case study is one that has some possibly controversial data.
I want you to carefully follow the discussion and develop your own
thoughts about the material being discussed. Some people may want to
avoid this type of conversation.
However, as a data scientist, we are often confronted with things
that have ethical concerns. So as you watch this video, continue to
monitor how our protagonist asks about the problem. And then also watch
how they raise their concerns. See if you have the same or different
concerns.
Feel free to submit those as part of your participation. Don’t
forget, important details about the problem are included in the video.
Once again, we are simulating a real world problem and a presentation
from a data science professional to a, this time, slightly more seasoned
data scientist. Again, we will pause to have you take a look at the
data.
And you can submit further questions towards the end. Those questions
can then be uploaded as part of your participation grade.
- Case Study 2, Part I
All right. So this week we have a problem coming to us from the
medical community. We’re looking specifically at a diabetes study. And
the problem is hospital readmission.
Now we don’t want people in hospitals. We want them to be well. And
we certainly don’t want them to be readmitted. This comes at a huge cost
to the patient in terms of bills, lost wages, strain on their family and
whatnot.
So our goal is no readmission. So for this study, what we’re trying
to do is we want to try to predict readmission of the patient within 30
days of initial hospitalization. So take a look at that data and we’ll
come back and take any questions you have.
- Case Study 2, Part II
So you’ve had a chance to look at the data. Do you have any questions
about it? I actually do have a couple of questions about this particular
assignment. And the first one is, well, pretty important to me.
It’s regarding the race category. And I don’t understand its
significance. It seems actually kind of like sorting patients with this
criteria could be seen as racially biased or something. I mean, simply
talking about identifying who was getting readmitted to the
hospital.
So I really don’t understand why race matters. OK. Absolutely. And
thank you for bringing that up because I absolutely agree with you.
Normally, this is not something that we would take into
consideration. Sorting by race can bring in a lot of ethical
considerations. In this case, we’re talking about the medical community.
We’re talking about patients.
And we do know that diabetes affects different demographics
differently. So race actually could very well be a factor in this. Now,
that being said, I will leave it to you. As long as we can chart the
trends accurately, I’m not as concerned about how we get there.
OK. Got it. Thank you. So my next question is all about all of these
question marks that are in the data set.
What’s going on with that? Yeah. The study took place over 10 years.
There’s something like 130 hospitals that they were pulling data
from.
There are a lot of people entering data into this study. And so there
are holes. So we’re just going to have to make the best recommendation
we can based on the data that we have. OK.
Understood. I’ll get working right away. Thank you.
You will submit your assignment to this page.
Please refer to your course syllabus for additional details about
this assignment.
When you save your assignment, the file name should be “First
Name_Last Name_Assignment Name.”
Your case study is to build classifiers using logistic regression to
predict ALL 3 CATEGORIES of hospital re-admittance. There is missing
data that must be imputed.
Once again, discuss the top 5 variable importance of your best model
as part of your submissio
What is Discrete Data?
Discrete data refers to a type of quantitative data
that consists of distinct, separate values. These values are countable
and cannot take on every possible value within a range. In other words,
discrete data is made up of whole numbers or specific categories, and
there are no intermediate values between two data points.
Key Characteristics of Discrete Data
- Countable: Discrete data represents items that can
be counted (e.g., the number of students in a class).
- Finite or Infinite: It can have a finite set of
values (e.g., dice rolls: 1, 2, 3, 4, 5, 6) or an infinite set (e.g.,
the number of times a coin is flipped until heads appears).
- No Fractions or Decimals: Discrete data cannot take
fractional or decimal values (e.g., you can’t have 2.5 students).
- Categorical or Numerical: While discrete data is
often numerical, it can also represent categories (e.g., types of cars:
sedan, SUV, truck).
Examples of Discrete Data
- Numerical Examples:
- Number of children in a family (e.g., 0, 1, 2, 3).
- Number of cars in a parking lot.
- Number of goals scored in a soccer match.
- Categorical Examples:
- Shoe sizes (e.g., 7, 8, 9, 10).
- Types of fruits in a basket (e.g., apples, oranges, bananas).
Discrete vs. Continuous Data
Definition |
Countable, distinct values |
Measurable, infinite values within a range |
Examples |
Number of students, dice rolls |
Height, weight, temperature |
Values |
Whole numbers or categories |
Can include fractions and decimals |
Graph Representation |
Bar charts, pie charts |
Histograms, line graphs |
How to Analyze Discrete Data
- Visualization:
- Use bar charts or pie charts to
represent discrete data visually.
- Example: A bar chart showing the number of students in each
grade.
- Statistical Measures:
- Calculate measures like mean,
median, mode, and
range.
- Example: The average number of cars sold by a dealership per
day.
- Probability:
- Discrete data is often used in probability distributions, such as
the binomial distribution or Poisson
distribution.
Applications of Discrete Data
- Business: Tracking the number of products sold or
customer complaints.
- Education: Counting the number of students in
different classes.
- Healthcare: Recording the number of patients
visiting a clinic daily.
- Sports: Counting goals, points, or wins in a
game.
Conclusion
Discrete data is a fundamental concept in statistics and data
analysis. It represents countable, distinct values and is often used in
scenarios where whole numbers or specific categories are involved.
Understanding discrete data is essential for analyzing and interpreting
real-world phenomena effectively.
Summary: Transitioning from Linear Regression to Logistic
Regression
This explanation focuses on the transition from linear
regression to logistic regression,
particularly when dealing with categorical data and
binary classification tasks.
- Problem with Linear Regression for Categorical
Targets:
- Linear regression predicts continuous outputs, which are not
suitable for categorical targets (e.g., predicting 0 or 1 for binary
classification).
- Even if the output is constrained between 0 and 1, linear regression
can still produce values outside this range, making it unreliable for
classification tasks.
- Discrete vs. Continuous Outputs:
- Continuous outputs vary smoothly with input values (e.g., linear
regression).
- Discrete outputs, like binary targets (0 or 1), only take specific
values and are better suited for classification tasks.
- Binary Classification:
- The target variable is binary (e.g., true/false, 0/1).
- One-hot encoding is used to transform categorical data into numeric
data, but linear regression cannot directly handle this for
classification.
- Introducing Logistic Regression:
- Logistic regression applies a variable
transformation to convert the continuous output of linear
regression into a discrete probability.
- This transformation is achieved using the sigmoid
function (denoted as σ), which maps any input to a value
between 0 and 1.
- Sigmoid Function:
- The sigmoid function takes the linear regression equation
(
y = mx + b
) as input and outputs a probability value
between 0 and 1.
- This ensures the output is suitable for binary classification
tasks.
- Compact Notation:
- Logistic regression builds on the familiar linear regression
equation (
y = m1x1 + m2x2 + ... + b
), but applies the
sigmoid function to the result.
- The summation sign is often implied for brevity, as the structure of
the equation remains similar to linear regression.
Key Takeaway:
Logistic regression extends linear regression by introducing a
sigmoid function to model binary categorical
outputs. This transformation ensures the output is constrained
between 0 and 1, making it suitable for classification tasks where the
target is discrete.
Summary: Transitioning from Linear Regression to Logistic
Regression
This explanation focuses on the transition from linear
regression to logistic regression,
particularly when dealing with categorical data and
binary classification tasks.
- Problem with Linear Regression for Categorical
Targets:
- Linear regression predicts continuous outputs, which are not
suitable for categorical targets (e.g., predicting 0 or 1 for binary
classification).
- Even if the output is constrained between 0 and 1, linear regression
can still produce values outside this range, making it unreliable for
classification tasks.
- Discrete vs. Continuous Outputs:
- Continuous outputs vary smoothly with input values (e.g., linear
regression).
- Discrete outputs, like binary targets (0 or 1), only take specific
values and are better suited for classification tasks.
- Binary Classification:
- The target variable is binary (e.g., true/false, 0/1).
- One-hot encoding is used to transform categorical data into numeric
data, but linear regression cannot directly handle this for
classification.
- Introducing Logistic Regression:
- Logistic regression applies a variable
transformation to convert the continuous output of linear
regression into a discrete probability.
- This transformation is achieved using the sigmoid
function (denoted as σ), which maps any input to a value
between 0 and 1.
- Sigmoid Function:
- The sigmoid function takes the linear regression equation
(
y = mx + b
) as input and outputs a probability value
between 0 and 1.
- This ensures the output is suitable for binary classification
tasks.
- Compact Notation:
- Logistic regression builds on the familiar linear regression
equation (
y = m1x1 + m2x2 + ... + b
), but applies the
sigmoid function to the result.
- The summation sign is often implied for brevity, as the structure of
the equation remains similar to linear regression.
Key Takeaway:
Logistic regression extends linear regression by introducing a
sigmoid function to model binary categorical
outputs. This transformation ensures the output is constrained
between 0 and 1, making it suitable for classification tasks where the
target is discrete. This slide introduces logistic
regression by building on the familiar linear
regression equation and applying a transformation to make it
suitable for classification tasks, particularly binary
classification.
1. Linear Regression Equation
The original linear regression equation is: \[
y = m_1x_1 + m_2x_2 + \dots = \sum m_ix_i
\] - Explanation: - \(m_i\): Coefficients (slopes) for each
feature \(x_i\). - \(x_i\): Input features (independent
variables). - \(y\): Output (dependent
variable), which is continuous in linear regression.
2. Problem with Linear Regression for
Classification
- Linear regression produces continuous outputs, which are not ideal
for classification tasks where the target is categorical (e.g., 0 or 1
for binary classification).
- To address this, we need to transform the output into a
probability that lies between 0 and 1.
3. Logistic Regression Equation
The logistic regression equation introduces the sigmoid
function to transform the output: \[
y = \frac{1}{1 + e^{-\sum m_ix_i}}
\] - Explanation: - The sigmoid function maps
any real number (from \(-\infty\) to
\(+\infty\)) to a value between 0 and
1. - This makes the output interpretable as a
probability for binary classification.
4. Compact Notation
The equation can be written more compactly as: \[
y = \sigma(\sum m_ix_i)
\] - \(\sigma\): The sigmoid function,
defined as: \[
\sigma(z) = \frac{1}{1 + e^{-z}}
\] - The summation sign (\(\sum\)) is often dropped for clarity, as it
is implied that the input is the weighted sum of features (\(m_ix_i\)).
Key Takeaways
- Linear regression is extended to logistic
regression by applying the sigmoid function to the output.
- The sigmoid function ensures the output is between 0 and 1, making
it suitable for binary classification tasks.
- Logistic regression predicts the probability of a
binary outcome (e.g., 0 or 1), where the decision boundary is typically
set at 0.5.
Summary and Expansion: Understanding the Sigmoid Function in
Logistic Regression
The sigmoid function is a key mathematical tool in transforming
linear regression into logistic regression. It maps continuous input
values into a range between 0 and 1, making it suitable for binary
classification tasks. Here’s a breakdown and deeper explanation of its
properties and applications:
1. What Does the Sigmoid Function Do?
- Purpose: The sigmoid function transforms the output
of a linear equation into a probability between 0 and 1.
- Equation: \[
y = \frac{1}{1 + e^{-\sum m_ix_i}}
\]
- \(m_i\): Coefficients (slopes) for
each input feature \(x_i\).
- \(\sum m_ix_i\): The linear
combination of features and coefficients.
- \(e\): The exponential
function.
- Behavior:
- For large positive values of \(\sum
m_ix_i\), the output approaches 1.
- For large negative values of \(\sum
m_ix_i\), the output approaches 0.
- For values near 0, the output is approximately 0.5.
This transformation creates an S-shaped curve
(sigmoid curve), which is ideal for modeling probabilities.
2. Relationship to Binary Classification
- Binary Outputs: Logistic regression predicts
probabilities for two classes:
- Class 1: Probability close to 1.
- Class 0: Probability close to 0.
- Thresholding:
- A default threshold of \(y = 0.5\)
is often used:
- \(y \geq 0.5\): Assign to Class
1.
- \(y < 0.5\): Assign to Class
0.
- However, the threshold can be adjusted based on the problem (e.g.,
fraud detection, medical diagnosis) to balance sensitivity and
specificity.
3. Properties of the Sigmoid Function
- Output Behavior:
- When \(\sum m_ix_i\) is very large
and positive:
- \(e^{-\text{large positive number}} \to
0\), so \(y \to 1\).
- When \(\sum m_ix_i\) is very large
and negative:
- \(e^{-\text{large negative number}} \to
\infty\), so \(y \to 0\).
- Smooth Transition:
- The sigmoid function smoothly transitions from 0 to 1, avoiding
abrupt changes like a step function.
- This smoothness allows the function to output probabilities, which
can then be thresholded for classification.
4. Adjusting the Threshold
- Default Threshold: \(y =
0.5\).
- Custom Thresholds:
- Lower thresholds (e.g., \(y =
0.2\)) increase sensitivity, useful for detecting rare events
like fraud.
- Higher thresholds (e.g., \(y =
0.9\)) increase specificity, useful for high-confidence
decisions.
- Importance of Thresholds:
- Choosing the right threshold is critical for balancing false
positives and false negatives, depending on the application’s
requirements.
5. Why the Sigmoid Function?
- Simulates a Step Function:
- At extreme values, the sigmoid function behaves like a step
function, producing outputs close to 0 or 1.
- Mathematical Simplicity:
- Despite involving exponentials, the sigmoid function has properties
that simplify calculations, making it computationally efficient.
- Probabilistic Interpretation:
- The output of the sigmoid function can be interpreted as the
probability of belonging to Class 1.
6. Practical Considerations
- Framing the Problem:
- Logistic regression is framed such that positive values of \(\sum m_ix_i\) correspond to Class 1, and
negative values correspond to Class 0.
- Applications:
- Logistic regression is widely used in applications like fraud
detection, medical diagnosis, and spam classification.
Conclusion
The sigmoid function is the cornerstone of logistic regression,
enabling the transformation of continuous linear outputs into
probabilities suitable for binary classification. Its smooth, S-shaped
curve and ability to simulate step-like behavior make it ideal for
modeling discrete outcomes. By adjusting thresholds and leveraging its
mathematical properties, the sigmoid function provides flexibility and
precision in a variety of real-world applications.
Summary of Logarithmic Loss and Its Derivation
Logarithmic Loss (Log Loss) is a critical loss function used in
classification problems, particularly for binary classification tasks.
It evaluates the performance of a classification model by penalizing
predictions that deviate from the true target probabilities. Here’s a
breakdown of the key ideas:
- Loss Functions in Regression:
- For regression, we typically use Mean Squared Error
(MSE) or Mean Absolute Error (MAE) to minimize
the vertical distance between predictions (\(\hat{y}\)) and true targets (\(y\)).
- These measure error using continuous distances between predicted and
actual values.
- Challenges in Classification:
- Classification involves discrete labels, such as \(y = 0\) or \(y =
1\).
- Predictions are probabilistic, e.g., \(p(y=1 | x)\), derived from models like
logistic regression with a sigmoid activation
function.
- Log Loss Function:
- The sigmoid function outputs probabilities (\(p\)) between 0 and 1: \[
p = \sigma(z) = \frac{1}{1 + e^{-z}}
\]
- The log loss function measures how close these probabilities are to
the true labels using the natural logarithm (\(\ln\)).
- Mathematical Derivation:
- Log loss for \(y=1\): \[
\text{Loss} = -\ln(p)
\]
- Log loss for \(y=0\): \[
\text{Loss} = -\ln(1-p)
\]
- Combined, for general \(y\)
(binary): \[
\text{Log Loss} = - \big( y \ln(p) + (1-y) \ln(1-p) \big)
\]
- Here, \(p\) is the model’s
predicted probability for \(y=1\), and
\(1-p\) for \(y=0\).
- Intuition:
- Penalizing incorrect predictions: Logarithms
amplify penalties for predictions that are far from the true label.
- A prediction of \(p=0.99\) for
\(y=1\) incurs a small loss, but \(p=0.01\) incurs a large loss.
- The log loss function is convex, ensuring a unique
global minimum.
- Visualization:
- The log loss curves for \(y=1\) and
\(y=0\) are mirror images, reflecting
their symmetry.
- The total loss function forms a convex shape, simplifying
optimization.
Mathematical Notation in Code
Python Code Example
Below is a Python implementation of the log loss function for binary
classification.
import numpy as np
def sigmoid(z):
"""Compute the sigmoid of z."""
return 1 / (1 + np.exp(-z))
def log_loss(y_true, y_pred):
"""
Compute the log loss for binary classification.
Parameters:
- y_true: True labels (0 or 1).
- y_pred: Predicted probabilities (between 0 and 1).
Returns:
- Average log loss.
"""
epsilon = 1e-15 # Avoid log(0) by clipping predictions
y_pred = np.clip(y_pred, epsilon, 1 - epsilon)
loss = -np.mean(y_true * np.log(y_pred) + (1 - y_true) * np.log(1 - y_pred))
return loss
# Example usage
y_true = np.array([1, 0, 1, 0]) # True labels
y_pred = np.array([0.9, 0.1, 0.8, 0.2]) # Predicted probabilities
print("Log Loss:", log_loss(y_true, y_pred))
Visualization in Python
The following Python code visualizes log loss for \(y=1\) and \(y=0\).
import matplotlib.pyplot as plt
# Probabilities from 0 to 1
p = np.linspace(0.01, 0.99, 100)
# Log loss for y=1 and y=0
loss_y1 = -np.log(p)
loss_y0 = -np.log(1 - p)
# Plot
plt.figure(figsize=(8, 6))
plt.plot(p, loss_y1, label='Loss (y=1)', color='blue')
plt.plot(p, loss_y0, label='Loss (y=0)', color='orange')
plt.title("Logarithmic Loss for Binary Classification")
plt.xlabel("Predicted Probability (p)")
plt.ylabel("Log Loss")
plt.legend()
plt.grid()
plt.show()
Summary of Key Insights
- Log Loss combines terms for \(y=1\) and \(y=0\) into a single, symmetric
function.
- It penalizes incorrect predictions more as they deviate from the
true class probability.
- The convexity of log loss ensures optimization is feasible with
methods like gradient descent.
This foundation makes log loss a standard for classification tasks
and a crucial metric for training and evaluating models like logistic
regression and neural networks.
Summary and Expansion: Logarithmic Loss (Log Loss) in Logistic
Regression
Logarithmic loss, or log loss, is a key loss function used in
logistic regression to evaluate the performance of a model when
predicting probabilities for binary classification
tasks. It measures how far the predicted probabilities are from
the actual target values (0 or 1).
1. Why Not Use Mean Squared Error (MSE)?
- Linear Regression: In linear regression, we use MSE
or Mean Absolute Error (MAE) to measure the distance between predictions
and targets. These methods work well for continuous outputs.
- Categorical Data: For binary classification, the
target values are either 0 or 1. Using MSE is not ideal because it
doesn’t account for the probabilistic nature of the predictions in
logistic regression.
- Need for Log Loss: Log loss is designed
specifically for classification tasks, penalizing incorrect predictions
more effectively by using probabilities.
2. Logarithmic Loss Function
The log loss function combines two cases: when the target \(y = 1\) and when \(y = 0\). It is defined as:
\[
\text{Log Loss} = - \left[ y \cdot \log(p) + (1 - y) \cdot \log(1 - p)
\right]
\]
- Key Components:
- \(p\): Predicted probability
(output of the sigmoid function).
- \(y\): Actual target value (0 or
1).
- \(\log\): Natural logarithm (base
\(e\)).
- Explanation:
- When \(y = 1\): The first term
\(-y \cdot \log(p)\) is active, and the
second term becomes zero.
- When \(y = 0\): The second term
\(-(1 - y) \cdot \log(1 - p)\) is
active, and the first term becomes zero.
- This ensures that only the relevant term contributes to the loss
based on the actual target.
3. Intuition Behind Log Loss
- Penalty for Incorrect Predictions:
- If the predicted probability \(p\)
is close to the actual target \(y\),
the loss is small.
- If \(p\) is far from \(y\), the loss is large.
- Behavior:
- For \(y = 1\), if \(p\) is close to 1, \(-\log(p)\) is small. If \(p\) is close to 0, \(-\log(p)\) becomes very large.
- For \(y = 0\), if \(p\) is close to 0, \(-\log(1 - p)\) is small. If \(p\) is close to 1, \(-\log(1 - p)\) becomes very large.
This penalization encourages the model to predict probabilities
closer to the true target.
4. Why Use Log Loss?
- Probabilistic Predictions:
- Logistic regression outputs probabilities using the sigmoid
function. Log loss evaluates these probabilities rather than discrete
predictions.
- Coupled Classes:
- Since binary classification involves two mutually exclusive classes
(0 and 1), log loss accounts for both cases simultaneously.
- Convexity:
- Log loss forms a convex curve with a well-defined
minimum, which is ideal for optimization algorithms like gradient
descent.
5. Visualization of Log Loss
- Loss for \(y =
1\):
- As \(p \to 1\), the loss approaches
0 (correct prediction).
- As \(p \to 0\), the loss becomes
very large (incorrect prediction).
- Loss for \(y =
0\):
- As \(p \to 0\), the loss approaches
0 (correct prediction).
- As \(p \to 1\), the loss becomes
very large (incorrect prediction).
- Combined Loss:
- The total log loss curve is convex, ensuring a single global
minimum.
6. Practical Applications
- Thresholding:
- Logistic regression predicts probabilities. A threshold (e.g., 0.5)
is used to classify probabilities into binary outcomes.
- Log loss ensures the model focuses on improving predictions close to
the threshold.
- Model Training:
- Minimizing log loss during training ensures the model outputs
probabilities that align closely with the true labels.
7. Advantages of Log Loss
- Handles Probabilities: Unlike MSE, it evaluates
probabilistic predictions effectively.
- Penalizes Confident Wrong Predictions: Predictions
far from the true target are penalized more heavily.
- Optimizable: The convex nature of log loss ensures
that optimization algorithms can find the global minimum.
Conclusion
Log loss is a critical tool for evaluating and training logistic
regression models. By penalizing incorrect predictions based on
probabilities, it ensures the model outputs probabilities that are both
accurate and reliable. Its mathematical properties and intuitive
behavior make it ideal for binary classification tasks.
Minimizing Log Loss: Summary and Detailed
Expansion
Conceptual Overview
Minimizing logarithmic loss (log loss) is a core
step in training logistic regression models. A smaller log loss
indicates that the model’s predicted probabilities closely align with
the true class labels, resulting in better classification
performance.
Key Components
- Log Loss Function:
- The log loss \(J\) for binary
classification is: \[
J(\mathbf{w}) = -\frac{1}{N} \sum_{i=1}^{N} \Big[ y_i \log(p_i) + (1 -
y_i) \log(1 - p_i) \Big]
\] where:
- \(N\) = number of data points,
- \(y_i\) = true label (\(0\) or \(1\)),
- \(p_i = \sigma(z_i) = \frac{1}{1 +
e^{-z_i}}\), the predicted probability for \(y=1\),
- \(z_i = \mathbf{w}^T
\mathbf{x}_i\), the linear combination of weights (\(\mathbf{w}\)) and features (\(\mathbf{x}_i\)).
- Prediction Function (Sigmoid):
- The sigmoid activation squashes the linear combination of inputs
into a probability: \[
\sigma(z) = \frac{1}{1 + e^{-z}}
\]
- Ensures \(0 < p < 1\),
suitable for probability estimation.
- Optimization Goal:
- Minimize \(J(\mathbf{w})\) by
adjusting the weights \(\mathbf{w}\),
which control the decision boundary of the classifier.
Mathematics of Minimizing Log Loss
To minimize \(J(\mathbf{w})\), we
apply gradient descent: 1. Gradient of Log Loss
w.r.t Weights: - The partial derivative of \(J\) with respect to \(\mathbf{w}\) is: \[
\frac{\partial J}{\partial \mathbf{w}} = \frac{1}{N} \sum_{i=1}^{N}
(\sigma(z_i) - y_i) \mathbf{x}_i
\] - Intuition: - \(\sigma(z_i) -
y_i\): The prediction error. - \(\mathbf{x}_i\): Feature vector influences
weight updates.
- Gradient Descent Update Rule:
- Update weights iteratively: \[
\mathbf{w} \leftarrow \mathbf{w} - \eta \frac{\partial J}{\partial
\mathbf{w}}
\]
- \(\eta\): Learning rate controls
step size.
Coding Representation
Here’s the Python implementation:
1. Sigmoid Function
import numpy as np
def sigmoid(z):
"""Compute sigmoid function."""
return 1 / (1 + np.exp(-z))
2. Log Loss Function
def log_loss(y_true, y_pred):
"""
Compute binary log loss.
Parameters:
- y_true: True binary labels (array-like).
- y_pred: Predicted probabilities (array-like).
Returns:
- Average log loss.
"""
epsilon = 1e-15 # To avoid log(0)
y_pred = np.clip(y_pred, epsilon, 1 - epsilon)
return -np.mean(y_true * np.log(y_pred) + (1 - y_true) * np.log(1 - y_pred))
3. Logistic Regression Using Gradient Descent
def logistic_regression(X, y, lr=0.01, epochs=1000):
"""
Train logistic regression using gradient descent.
Parameters:
- X: Feature matrix (N x d).
- y: Labels (N x 1).
- lr: Learning rate.
- epochs: Number of iterations.
Returns:
- weights: Trained weights.
- losses: Log loss per epoch.
"""
N, d = X.shape
weights = np.zeros(d) # Initialize weights
losses = []
for epoch in range(epochs):
# Linear combination
z = np.dot(X, weights)
# Predicted probabilities
preds = sigmoid(z)
# Compute log loss
loss = log_loss(y, preds)
losses.append(loss)
# Gradient calculation
gradient = np.dot(X.T, (preds - y)) / N
# Weight update
weights -= lr * gradient
return weights, losses
4. Visualization of Log Loss Minimization
import matplotlib.pyplot as plt
# Generate synthetic data
np.random.seed(42)
X = np.random.rand(100, 2)
y = (X[:, 0] + X[:, 1] > 1).astype(int)
# Train logistic regression
weights, losses = logistic_regression(X, y, lr=0.1, epochs=200)
# Plot log loss
plt.plot(losses)
plt.title("Log Loss Minimization")
plt.xlabel("Epochs")
plt.ylabel("Log Loss")
plt.grid()
plt.show()
Key Insights
- Convexity of Log Loss:
- Log loss is convex, ensuring a global minimum.
- Gradient descent is guaranteed to converge with a proper learning
rate.
- Weight Updates and Error:
- Each weight update reduces the prediction error by moving in the
direction opposite to the gradient.
- Relationship to Linear Regression:
- Logistic regression extends linear regression by squashing
predictions into probabilities using the sigmoid function.
- The gradient update rule is structurally similar, differing mainly
in the sigmoid nonlinearity.
This mathematical and coding breakdown highlights the intuitive and
practical aspects of minimizing log loss, making logistic regression a
robust tool for binary classification tasks.
Summary and Expansion: Minimizing Log Loss in Logistic
Regression
This explanation focuses on how to minimize the log loss
function in logistic regression, which is essential for
training the model to make accurate predictions. The process involves
leveraging optimization techniques, particularly gradient descent, to
adjust the model’s parameters (slopes) and achieve a well-fit model.
1. Objective of Minimizing Log Loss
- Goal: Minimizing log loss ensures that the model
produces predictions that are as close as possible to the actual target
values.
- Small Loss = Good Model: A smaller log loss
indicates a well-trained model with accurate predictions.
2. Log Loss Function Recap
- The log loss function (\(J\)) is
defined as: \[
J = - \frac{1}{n} \sum_{i=1}^n \left[ y_i \cdot \log(p_i) + (1 - y_i)
\cdot \log(1 - p_i) \right]
\]
- \(y_i\): Actual target (0 or
1).
- \(p_i\): Predicted probability
(output of the sigmoid function).
- \(n\): Number of data points.
- Key Insight:
- \(p\) is not just a number; it is
the output of the sigmoid function: \[
p = \sigma(m \cdot x) = \frac{1}{1 + e^{-m \cdot x}}
\]
- The sigmoid function depends on the input data (\(x\)) and the model parameters (\(m\), the slopes).
3. Minimizing Log Loss
- To minimize log loss, the process involves:
- Fixing the Data:
- The target values (\(y\)) and input
data (\(x\)) are fixed and cannot be
changed.
- Adjusting the Slopes:
- The slopes (\(m\)) are the only
variables that can be updated to minimize \(J\).
4. Gradient Descent for Optimization
- Gradient Descent:
- Gradient descent is used to find the minimum of the log loss
function.
- The partial derivative of \(J\)
with respect to each slope (\(m_i\))
determines the direction and magnitude of the update.
- The update rule is: \[
m_i = m_i - \alpha \cdot \frac{\partial J}{\partial m_i}
\]
- \(\alpha\): Learning rate (controls
the step size of the update).
- \(\frac{\partial J}{\partial
m_i}\): Partial derivative of \(J\) with respect to \(m_i\).
- Key Insight:
- The derivative of the log loss function with the sigmoid function is
mathematically structured such that the update rule is
identical to that of linear regression. This is due to
the choice of the sigmoid and log functions.
5. Why the Sigmoid and Log Functions?
- The sigmoid and log functions were specifically chosen because:
- The log loss function is convex, ensuring a single
global minimum.
- The derivative of the natural log (\(\log_e\)) is simple: \[
\frac{d}{dp} \log(p) = \frac{1}{p}
\]
- This simplicity ensures that the update rules for logistic
regression align with those for linear regression, allowing the same
optimization algorithms to be reused.
6. Practical Implications
- Unified Optimization:
- The fact that logistic regression shares the same update rule as
linear regression simplifies implementation.
- Algorithms like stochastic gradient descent (SGD) or batch gradient
descent can be directly applied.
- Extending Linear Regression:
- By simply changing the loss function (from mean squared error to log
loss), logistic regression extends linear regression to handle
categorical problems.
7. Key Takeaways
- Minimizing Log Loss:
- Achieved by adjusting the slopes (\(m\)) using gradient descent.
- The convexity of the log loss function ensures an easy-to-find
global minimum.
- Reusability:
- The derivative of the log loss function aligns with linear
regression’s update rules, making optimization straightforward.
- Why the Sigmoid Function?:
- The sigmoid function ensures outputs are probabilities (between 0
and 1).
- Its exponential structure simplifies derivatives, which is crucial
for efficient optimization.
Conclusion
Minimizing log loss in logistic regression is a straightforward
extension of linear regression’s optimization process. By using the
sigmoid function and log loss, the model can handle categorical data
effectively while maintaining the same optimization framework. This
mathematical design ensures both simplicity and efficiency in training
logistic regression models.
Multiclass Classification: Overview and
Explanation
Multiclass classification extends binary classification techniques to
problems where there are more than two classes. For instance, instead of
determining if an input belongs to “cat” or “dog,” we might classify
between “cat,” “dog,” and “rabbit.”
Key Approaches to Multiclass Classification
1. One-vs-Rest (OvR) Method
- Concept: Train separate binary classifiers for each
class.
- For \(K\) classes, we train \(K\) models:
- \(\text{Class}_i\) vs. “not-\(\text{Class}_i\)”
- Example:
- Classes: Red, Green, Blue.
- Models: Red-vs-not-Red, Green-vs-not-Green, Blue-vs-not-Blue.
- Prediction: Use the argmax
function to choose the class with the highest probability: \[
\hat{y} = \text{argmax}_k \; \text{sigmoid}(\mathbf{w}_k^T \mathbf{x})
\]
- Pros:
- Simple and easy to implement.
- Works well for a small number of classes.
- Cons:
- Bias towards the negative class when class distributions are
imbalanced.
- Scales poorly with the number of classes.
2. One-vs-One (OvO) Method
- Concept: Train a binary classifier for each pair of
classes.
- For \(K\) classes, train \(\binom{K}{2} = \frac{K(K-1)}{2}\) models.
- Each model distinguishes between two classes, e.g., \(\text{Class}_i\) vs. \(\text{Class}_j\).
- Example:
- Classes: Red, Green, Blue.
- Models: Red-vs-Green, Red-vs-Blue, Green-vs-Blue.
- Prediction: Use majority voting:
- Each classifier “votes” for one class.
- The class with the most votes wins.
- Pros:
- Handles imbalanced classes better than OvR.
- Often performs well for small \(K\).
- Cons:
- Computationally expensive for large \(K\) due to the \(O(K^2)\) number of models.
- Potential for ties or paradoxical results in voting.
Mathematical Representation
Log Loss for Multiclass Problems
For a dataset with \(K\) classes: 1.
Softmax Activation: - Generalizes the sigmoid function
for multiclass: \[
p(y=k|\mathbf{x}) = \frac{e^{\mathbf{w}_k^T
\mathbf{x}}}{\sum_{j=1}^K e^{\mathbf{w}_j^T \mathbf{x}}}
\] - Outputs a probability distribution over \(K\) classes.
- Cross-Entropy Loss:
- Measures the difference between predicted and true probability
distributions: \[
J(\mathbf{W}) = -\frac{1}{N} \sum_{i=1}^N \sum_{k=1}^K y_{i,k}
\log(p(y=k|\mathbf{x}_i))
\]
- \(\mathbf{W} = [\mathbf{w}_1,
\mathbf{w}_2, \ldots, \mathbf{w}_K]\): weight matrix for \(K\) classes.
- \(y_{i,k}\): One-hot encoded true
label for \(\mathbf{x}_i\).
Python Implementation
Softmax Function
def softmax(z):
"""
Compute the softmax of vector z.
Parameters:
- z: 2D array of shape (N, K), where N is the number of samples and K is the number of classes.
Returns:
- Softmax probabilities for each class.
"""
exp_z = np.exp(z - np.max(z, axis=1, keepdims=True)) # Stabilize for numerical safety
return exp_z / np.sum(exp_z, axis=1, keepdims=True)
Cross-Entropy Loss
def cross_entropy_loss(y_true, y_pred):
"""
Compute the cross-entropy loss for multiclass classification.
Parameters:
- y_true: One-hot encoded true labels of shape (N, K).
- y_pred: Predicted probabilities of shape (N, K).
Returns:
- Average cross-entropy loss.
"""
epsilon = 1e-15 # Avoid log(0)
y_pred = np.clip(y_pred, epsilon, 1 - epsilon)
return -np.mean(np.sum(y_true * np.log(y_pred), axis=1))
Multiclass Logistic Regression Training
def train_multiclass_logistic_regression(X, y, lr=0.01, epochs=1000):
"""
Train a multiclass logistic regression model using gradient descent.
Parameters:
- X: Feature matrix (N x d).
- y: One-hot encoded labels (N x K).
- lr: Learning rate.
- epochs: Number of iterations.
Returns:
- W: Trained weight matrix (d x K).
- losses: List of cross-entropy losses per epoch.
"""
N, d = X.shape
K = y.shape[1]
W = np.zeros((d, K)) # Initialize weights
losses = []
for epoch in range(epochs):
# Linear combination
z = np.dot(X, W)
# Softmax probabilities
probs = softmax(z)
# Compute loss
loss = cross_entropy_loss(y, probs)
losses.append(loss)
# Gradient calculation
gradient = np.dot(X.T, (probs - y)) / N
# Weight update
W -= lr * gradient
return W, losses
Visualization of Loss Minimization
import matplotlib.pyplot as plt
# Generate synthetic data
np.random.seed(42)
X = np.random.rand(100, 3)
y_raw = np.random.randint(0, 3, 100) # Random labels (0, 1, 2)
y_one_hot = np.eye(3)[y_raw] # One-hot encode labels
# Train logistic regression
W, losses = train_multiclass_logistic_regression(X, y_one_hot, lr=0.1, epochs=300)
# Plot loss
plt.plot(losses)
plt.title("Cross-Entropy Loss Minimization")
plt.xlabel("Epochs")
plt.ylabel("Loss")
plt.grid()
plt.show()
Key Insights
- Softmax for Multiclass:
- Softmax ensures the sum of predicted probabilities equals 1,
suitable for multiclass problems.
- One-vs-Rest:
- Simple to implement, but scales poorly for large \(K\).
- One-vs-One:
- Computationally expensive but useful for small \(K\).
- Cross-Entropy Loss:
- The loss function generalizes binary cross-entropy for multiclass
problems.
- Optimization:
- Gradient descent works well due to the convex nature of the
cross-entropy loss.
This mathematical and coding explanation provides a comprehensive
understanding of multiclass classification using logistic
regression.
LS0tDQp0aXRsZTogIlFUVyA3MzMzIE1vZHVsZSAzIC0gVGFraW5nIGl0IEZ1cnRoZXIiDQpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sNCi0tLQ0KDQoNCiAzOiBMb2dpc3RpYyBSZWdyZXNzaW9uDQoNCkNhdGVnb3JpY2FsIERhdGENCg0KTGluZWFyIHRvIExvZ2lzdGljDQoNClRoZSBTaWdtb2lkDQoNCkxvZyBMb3NzDQoNCk1pbmltaXppbmcgTG9nIExvc3MNCg0KTXVsdGljbGFzcw0KQ29udGV4dCBNb2R1bGUgU3ViIEhlYWRlcg0KRGVtb25zdHJhdGlvbiBvZiBMb2dpc3RpYyBSZWdyZXNzaW9uDQoNCkRlbW9uc3RyYXRpb24gb2YgTG9naXN0aWMgUmVncmVzc2lvbiwgUGFydCBJDQoNCkRlbW9uc3RyYXRpb24gb2YgTG9naXN0aWMgUmVncmVzc2lvbiwgUGFydCBJSQ0KQ29udGV4dCBNb2R1bGUgU3ViIEhlYWRlcg0KQ2FzZSBTdHVkeSAyDQoNCkNhc2UgU3R1ZHkgMiBJbnRyb2R1Y3Rpb24NCg0KQ2FzZSBTdHVkeSAyLCBQYXJ0IEkNCg0KQ2FzZSBTdHVkeSAyLCBQYXJ0IElJDQpBc3NpZ25tZW50DQpDYXNlIFN0dWR5IDIgQXNzaWdubWVudA0KDQoNCg0KMS4gY2F0ZWdvcmljYWwgZGF0YQ0KDQpIaSBhbmQgd2VsY29tZSB0byB0aGlzIHZpZGVvIG9uIGNhdGVnb3JpY2FsIGRhdGEsIHdoZXJlIHdlJ2xsIHdlIGJlIHRhbGtpbmcgYWJvdXQgY2F0ZWdvcmljYWwgdGFyZ2V0cyBhbmQgY2F0ZWdvcmljYWwgaW5wdXRzLiBTbyBjYXRlZ29yaWNhbCBkYXRhIGlzIHdoZW4gd2UgaGF2ZSBkYXRhIHRoYXQncyBiYXNlZCBvbiBjbGFzc2VzLiBHb29kIGV4YW1wbGVzIG9mIHRoaXMgYXJlIHRoaW5ncyBsaWtlIGNvbG9ycy0tDQoNCmlzIHNvbWV0aGluZyByZWQsIGdyZWVuLCBvciBibHVlPy0tDQoNCm9yIGxvZ2ljLS0NCg0KaXMgc29tZXRoaW5nIHRydWUgb3IgZmFsc2U/IE90aGVyIGV4YW1wbGVzIGFyZSB0aGluZ3MgbGlrZSBvYmplY3RzLiBJcyBpdCBhIGNhcj8gSXMgaXQgYSBob3VzZT8NCg0KSXMgaXQgYSB0cnVjaz8gU28gdGhpcyBkYXRhLCB3aGVuIHdlIHNlZSBpdCBpbml0aWFsbHktLQ0KDQpyZWQsIGdyZWVuLCBhbmQgYmx1ZS0tDQoNCml0J3Mgbm9uLW51bWVyaWMuIEJ1dCBhbGwgb3VyIGFsZ29yaXRobXMgYWN0dWFsbHkgdGFrZSBudW1lcmljIGlucHV0cy4gU28gaG93IGFyZSB3ZSBnb2luZyB0byB0YWtlIHRoaXMgYm90aCBpbnB1dHMgYW5kIHBvc3NpYmx5IHRhcmdldHMgYXMgd2UgY291bGQgYmUgcHJlZGljdGluZyB3aGV0aGVyIHNvbWV0aGluZyBpcyBnb2luZyB0byBiZSBhIHJlZCBvYmplY3QsIGEgZ3JlZW4gb2JqZWN0LCBvciBhIGJsdWUgb2JqZWN0PyBJdCBjYW4gYm90aCBnbyBvbiBib3RoIHNpZGVzIG9mIHRoZSBlcXVhdGlvbiwgYm90aCB0aGUgaW5wdXRzIGFuZCB0aGUgdGFyZ2V0cy4NCg0KU28gaG93IGRvIHdlIG1ha2UgdGhpcyBkYXRhIGludG8gbnVtZXJpYyBkYXRhPyBUaGUgZXhhbXBsZSBvciB0aGUgc3RhbmRhcmQgbWV0aG9kIGlzIHdoYXQgd2UncmUgZ29pbmcgdG8gY2FsbCBvbmUtaG90IGVuY29kaW5nLiBTbyBpbiBlc3NlbmNlLCB3aGF0IHdlJ3JlIGdvaW5nIHRvIGRvIGlzIGNyZWF0ZSBuZXcgaW5kaWNhdG9yIHZhcmlhYmxlcywgYW5kIHRob3NlIGluZGljYXRvciB2YXJpYWJsZXMsIHRoZSBvYmplY3QgdHlwZS0tDQoNCm9yLCBpbiB0aGlzIGNhc2UsIGNvbG9yLS0NCg0Kd2lsbCBiZSB0aGUgY29sdW1uIG5hbWUuIFNvIHdlJ3JlIGdvaW5nIHRvIGhhdmUgdGhpcyBmZWF0dXJlLiBCYXNpY2FsbHksIGlzIGl0IHJlZCwgaXMgaXQgZ3JlZW4sIGlzIGl0IGJsdWUsIG9yIGlzIGl0IHBpbms/IFRoaXMgaXMgb25lLWhvdCBlbmNvZGluZyBhbmQgYmVjb21lcyBlc3NlbnRpYWxseSBhIHZlY3Rvciwgd2hlcmUgdGhlIHZlY3RvciByZXByZXNlbnRzIGFsbCB0aGUgZGlmZmVyZW50IHZhbHVlcy4NCg0KU28gdmVjdG9ycyBjYW4gYmUgc29tZXRpbWVzIGEgbGl0dGxlIGJpdCBpbnRpbWlkYXRpbmcuIEJ1dCB3aGF0IHdlIG5lZWQgdG8gdGhpbmsgb2YgaXMgd2UgYXJlIGp1c3Qgc2ltcGx5IGNyZWF0aW5nIG5ldyB2YXJpYWJsZXMgdGhhdCBzYXksIGlzIGl0IHJlZCwgeWVzIG9yIG5vPyBTbyBhcyB3ZSBzZWUgaW4gdGhlIGV4YW1wbGUgaGVyZSwgdGhlIGZpcnN0IHR3byBsaW5lcyBhcmUgcmVkLiBTbyB3ZSBzZWUgYSAxIGluIHRoZSByZWQgY29sdW1uLg0KDQpUaGV5J3JlIG5vdCBncmVlbiwgbm90IGJsdWUsIGFuZCBub3QgcGluaywgc28gYWxsIG9mIHRob3NlIGZlYXR1cmVzIGFyZSAwLiBXZSBjYW4gc2VlLCBhcyB3ZSBnbyBkb3duLCBlYWNoIHRpbWUgdGhlIGNvbG9yIGFwcGVhcnMsIHRoZXJlJ3MgYSAxIGluIHRoZSBhcHByb3ByaWF0ZSBjb2x1bW4uIFNvIHdlIHNlZSB0aGlzIGFzIGEgdmVjdG9yLiBTbyB0aGUgY29sb3IgcmVkIGJlY29tZXMgYSB2ZWN0b3IgMSwgMCwgMCwgMCwgYW5kIHRoZW4gYmx1ZSBiZWNvbWVzIHRoZSB2ZWN0b3IgMCwgMCwgMSwgMC4NCg0KQW5kIHRoaXMgYWxsb3dzIHVzIHRvIHRyYW5zZm9ybSB0aGVzZSBjYXRlZ29yaWVzIG9yIGZlYXR1cmVzIGludG8gbnVtZXJpYyBkYXRhLiBBbmQgbW9zdCBpbXBvcnRhbnRseSwgbm90IG9ubHkgZG9lcyBpdCBhbGxvdyB1cyB0byBjaGFuZ2UgZmVhdHVyZXMgaW50byBkYXRhLCBpdCBhbGxvd3MgdXMgdG8gdXNlIHRoZW0gYXMgdGFyZ2V0cyBhcyB3ZWxsLiBTbyB3ZSBtYXkgbm90IGJlIHVzZWQgdG8gc2VlaW5nIHRhcmdldHMgYXMgdmVjdG9ycywgYnV0IHRvd2FyZHMgdGhlIGVuZCBvZiBvdXIgdmlkZW8gc2VyaWVzLCB3ZSdsbCBzZWUgaG93IHRoaXMgcGxheXMgb3V0LiBBbmQgd2UgY2FuIGFjdHVhbGx5IHByZWRpY3QgbXVsdGlwbGUgY2F0ZWdvcmllcyBiYXNlZCBvbiB0aGlzIHNhbWUgY29uY2VwdCBvZiBpcyBpdCByZWQsIGlzIGEgZ3JlZW4sIGlzIGl0IGJsdWUsIGlzIGl0IHBpbms/DQoNCk5vdywgYW4gaW1wb3J0YW50IHRoaW5nIHRvIG5vdGUgaXMgb25seSBvbmUgY29sdW1uIHNob3VsZCBoYXZlIHRoZSAxIGluIGl0LiBBbGwgdGhlIG90aGVyIGNvbHVtbnMgc2hvdWxkIGJlIDAuIElmIHlvdSBkbyBoYXBwZW4gdG8gZmluZCBmZWF0dXJlcyB0aGF0IGFyZSBib3RoLS0NCg0KdGhhdCBoYXZlIG11bHRpcGxlIGNvbHVtbnMgdGhhdCBhcmUgMSwgbWFrZSBzdXJlIHRoYXQgdGhhdCdzIHdoYXQgeW91IHJlYWxseSBpbnRlbmQuIEluIHRoaXMgY2FzZSwgcmVkLCBncmVlbiwgYmx1ZSwgYW5kIHBpbmsgYXJlIGFsbCBleGNsdXNpdmUuIEJ1dCBwZXJoYXBzLCBpZiB5b3Ugd2FudGVkIHRvIGJyZWFrIGl0IGludG8gcHJpbWFyeSBjb2xvcnMgYW5kIHNheSwgb2gsIEkgaGF2ZSByZWQgYW5kIGJsdWUgYW5kIHRoYXQgd2lsbCBpbmRpY2F0ZSBwdXJwbGUsIHdlbGwsIHRoZW4sIHlvdSB3b3VsZCBoYXZlIGEgdmVjdG9yIDEsIDAsIDEuIFRoYXQncyBleHRyZW1lbHkgdW5jb21tb24uDQoNClVzdWFsbHksIGZvciBlYWNoIHVuaXF1ZSB2YWx1ZSBvZiB0aGUgZmVhdHVyZSwgd2UgYWRkIGEgY29sdW1uLiBBbmQgb25seSBvbmUgb2YgdGhlIGNvbHVtbnMgd2lsbCBoYXZlIGEgMSBpbmRpY2F0ZWQsIGFuZCB0aGUgcmVzdCBvZiB0aGUgY29sdW1ucyB3aWxsIGJlIDAuIEJ1dCB0aGF0IGlzIG5vdCBhIGhhcmQgYW5kIGZhc3QgcnVsZS4gSXQncyBtb3JlIG9mIGEgZ3VpZGVsaW5lLg0KDQpTbyBjYXRlZ29yaWNhbCBkYXRhIGlzIGNsb3NlbHkgcmVsYXRlZCB0byBkaXNjcmV0ZSBkYXRhLCBidXQgdGhleSBhcmUgbm90IHRoZSBzYW1lLiBBbmQgYSBnb29kIGV4YW1wbGUgb2YgdGhpcyBpcyB0aGUgbnVtYmVyIG9mIHJvb21zIGluIGEgaG91c2UuIElzIGl0IGNhdGVnb3JpY2FsPyBUaGUgYW5zd2VyIGlzIGFjdHVhbGx5IG5vLg0KDQpFdmVuIHRob3VnaCB3ZSBkb24ndCBoYXZlIGxpa2UgMyBhbmQgMS8yIHJvb21zIGluIGEgaG91c2Ugb3IgMy4yMSByb29tcywgd2hhdCB3ZSBoYXZlIGhlcmUgaXMgZGlzY3JldGUgZGF0YS4gU28gZGlzY3JldGUgZGF0YSwgaXQncyBpbXBvcnRhbnQgdG8gc3RpbGwgdHJlYXQgaXQgYXMgbnVtZXJpYyBiZWNhdXNlLCBpbiB0aGlzIGNhc2UsIHRoZSB2YWx1ZSBvZiAzIGFjdHVhbGx5IGluZGljYXRlcyBzb21ldGhpbmcgdGhhdCBpcyBncmVhdGVyIHRoYW4gdGhlIHZhbHVlIG9mIDIuIFRoZXJlIGFyZSB0aHJlZSByb29tcyB2ZXJzdXMgdHdvIHJvb21zLg0KDQpOb3csIGlmIHlvdSBoYXZlIGRpZmZlcmVudCBjbGFzc2VzLS0NCg0Kbm93IEknZCBzYXkgSSBoYXZlIGNsYXNzIDEgb3IgY2xhc3MgMiBvciwgaW4gdGhpcyBleGFtcGxlLCBjYXRlZ29yeSAxIG9yIGNhdGVnb3J5IDItLQ0KDQppcyBpdCByZWFsbHkgdGhhdCBjYXRlZ29yeSAyIHJlcHJlc2VudHMgdHdpY2UgdGhlIHZhbHVlIG9mIGNhdGVnb3J5IDE/IFNvIHRoYXQncyBob3cgeW91IGNhbiB0ZWxsIHRoZSBkaWZmZXJlbmNlIGJldHdlZW4gZGlzY3JldGUgZGF0YSBhbmQgY2F0ZWdvcmljYWwgZGF0YS4gQ2F0ZWdvcmljYWwgZGF0YSBuZWVkcyB0byBiZSBvbmUtaG90IGVuY29kZWQuIERpc2NyZXRlIGRhdGEsIHdoaWxlIHlvdSBjYW4gb25lLWhvdCBlbmNvZGUgaXQsIGNhbiBiZSBsZWZ0IGluIGl0cyBvcmlnaW5hbCBpbnRlZ2VyIGZvcm0uDQoNClNvIHJlbWVtYmVyIHRoYXQgd2hlbiB5b3UncmUgbG9va2luZyBhdCB5b3VyIHBvc3NpYmx5IGNhdGVnb3JpY2FsIGRhdGEgdG8gZW5zdXJlIHRoYXQgaXQncyBub3QgYWNjaWRlbnRhbGx5IGRpc2NyZXRlIGRhdGEuIFNvIGEgY291cGxlIG9mIGV4YW1wbGVzIG9mIGRpc2NyZXRlIGRhdGEuIFdlIHRhbGtlZCBhYm91dCB0aGUgbnVtYmVyIG9mIHJvb21zIGluIGEgaG91c2Ugb3IgdGhlIG51bWJlciBvZiBjYXJzIGEgZmFtaWx5IG93bnMuIFlvdSBkb24ndCBvd24gMiBhbmQgMS8yIGNhcnMgb3IgNC4xDQoNCmNhcnMuIEhvdyBtYW55IGNoaWxkcmVuIGRvIHlvdSBoYXZlPyBUaGUgYXZlcmFnZSBmYW1pbHkgaGFzIDIuNCBjaGlsZHJlbi4gVGhhdCdzIG5vdCBhbiBhY3R1YWwgbnVtYmVyIG9mIGNoaWxkcmVuLg0KDQpUaGUgbnVtYmVyIG9mIGNoaWxkcmVuIGlzIGRpc2NyZXRlLiBOdW1iZXIgb2YgY3lsaW5kZXJzIGluIGEgY2FyIGVuZ2luZS4gQ2FycyBkb24ndCBjb21lIHdpdGggYSBwYXJ0aWFsIGN5bGluZGVyLiBUaGV5IGhhdmUgdHdvLCBmb3VyLCBzb21ldGltZXMgZml2ZSBmb3IgdGhlIHJlYWxseSBvZGQgb25lcywgYnV0IHRoZSBjeWxpbmRlcnMgaW4gYSBjYXIgZW5naW5lIGlzIGRpc2NyZXRlLg0KDQpTbyByZW1lbWJlciwgZG8gbm90IG9uZS1ob3QgZW5jb2RlIGRpc2NyZXRlIGRhdGEuIEFsbG93IHRob3NlIHZhbHVlcyB0byBpbmZvcm0geW91ciBtb2RlbC4gSXQncyBwb3NzaWJsZSB5b3UgY2FuIGRvIGEgZGF0YSB0cmFuc2Zvcm0sIGJ1dCB0aGUgYmVzdCB3YXkgdG8gaGFuZGxlIHRoaXMgZGlzY3JldGUgZGF0YSBpcyB0byBpbnB1dCBpdCBkaXJlY3RseS4gU28gb25jZSB5b3UndmUgYWN0dWFsbHkgb25lLWhvdCBlbmNvZGVkIHlvdXIgY2F0ZWdvcmljYWwgZmVhdHVyZXMsIHlvdSBjYW4gdXNlIHRoaXMgaW4gZmFpcmx5IGFueSBtb2RlbC4NCg0KU28gaWYgd2UncmUgdHJ5aW5nIHRvIGZpZ3VyZSBvdXQgdGhlIHByaWNlIG9mIG91ciBjYXIsIGFuZCB3ZSdyZSB1c2luZyBhIGxpbmVhciByZWdyZXNzaW9uIG1vZGVsLCB3aGljaCB3ZSBsZWFybmVkIGFib3V0IGluIG9uZSBvZiB0aGUgZWFybGllciB3ZWVrcywgYW5kIG9uZSBvZiB0aGUgZmVhdHVyZXMgaXMgdGhlIGNhciBjb2xvci4gU28gYXMgd2UgbG9vayBhdCB0aGUgY29sb3Igb2YgdGhlIGNhciwgd2UgbWF5IGhhdmUgYmxhY2sgY2FycyBhbmQgc2lsdmVyIGNhcnMgYW5kIGdyYXkgY2Fycywgd2hhdCB3ZSdyZSBnb2luZyB0byBkbyBpcyB3ZSdyZSBnb2luZyB0byB0cmFuc2Zvcm0gdGhhdCBzaW5nbGUgY29sdW1uIG9mIGNhciBjb2xvcnMgdG8gbXVsdGlwbGUgY29sdW1ucywgd2hlcmUgZWFjaCBjb2x1bW4gaXMgYSBjb2xvciwgYW5kIHRoZXJlIHdpbGwgYmUgYSAxIHRvIGluZGljYXRlLCBmb3IgYSByb3csIGlmIHRoYXQgcGFydGljdWxhciBjYXIgaXMgdGhhdCBjb2xvci4gU28gSSBvd24gYSBibGFjayBjYXIuIFNvIHVuZGVybmVhdGggdGhlIEJsYWNrIGNvbHVtbiwgdGhlcmUgd291bGQgYmUgYSAxLg0KDQpBbmQgbGV0J3Mgc2F5IG91ciBvdGhlciBjYXIgY29sb3JzIGFyZSByZWQgYW5kIHdoaXRlLiBVbmRlciB0aGUgUmVkIGFuZCBXaGl0ZSBjb2x1bW5zLCB0aGVyZSB3b3VsZCBiZSAwJ3MuIFNvIG5vdyBJIGNhbiBwdXQgdGhpcyBpbnRvIGEgbGluZWFyIG1vZGVsLiBJIGNhbiBwdXQgaXQgaW50byBhbnkgbW9kZWwuDQoNCk91ciBjYXRlZ29yaWVzIGhhdmUgYmVlbiB0cmFuc2Zvcm1lZCBpbnRvIG51bWJlcnMgdGhhdCBvdXIgbW9kZWwgY2FuIGFjdHVhbGx5IHVuZGVyc3RhbmQuDQoNCg0KMi4gbGluZWFyIHRvIGxvZ2lzdGljDQpXZWxjb21lIHRvIG91ciB2aWRlbyB0aGF0J3MgZ29pbmcgdG8gdGFrZSB1cyBmcm9tIGxpbmVhciByZWdyZXNzaW9uIHRvIGxvZ2lzdGljIHJlZ3Jlc3Npb24uIFNvIHdlJ3ZlIGVuY291bnRlcmVkIGNhdGVnb3JpY2FsIGRhdGEgbm93LCBhbmQgdGhlIHF1ZXN0aW9uIGJlY29tZXMsIHdoYXQgaGFwcGVucyBpZiB0aGUgdGFyZ2V0IG9yIG91ciBwcmVkaWN0aW9uIGlzIGFjdHVhbGx5IG9uZS1ob3QgZW5jb2RlZD8gU28gdGhlIGlkZWEgYmVpbmcsIGxldCdzIHRyeSBhbmQgcHJlZGljdCBpZiBzb21ldGhpbmcgaXMgdHJ1ZSBvciBzb21ldGhpbmcgaXMgZmFsc2UuIEFuZCB3ZSBzYXcsIGluIHRoZSBwYXN0LCB0aGF0IHdlIG9uZS1ob3QgZW5jb2RlIHRoZXNlLCBhbmQgdGhhdCBjYW4gdHJhbnNmb3JtIG91ciBjYXRlZ29yaWNhbCBkYXRhIGludG8gbnVtZXJpYyBkYXRhLg0KDQpCdXQsIG5vdywgd2hhdCBoYXBwZW5zIGlmIG91ciB0YXJnZXQgaXMgbm93IHRoaXMgY2F0ZWdvcnk/IFdlJ3ZlIG9uZS1ob3QgZW5jb2RlZCB0aGUgdGFyZ2V0LCBidXQsIHJlbWVtYmVyLCBsaW5lYXIgcmVncmVzc2lvbiBkb2Vzbid0IHByZWRpY3QgYSB6ZXJvIG9yIGEgb25lLiBMaW5lYXIgcmVncmVzc2lvbiBwcm9kdWNlcyBhIGNvbnRpbnVvdXMgb3V0cHV0LiBTbyBldmVuIGlmIHdlIG1hbmFnZSB0byBnZXQgdGhhdCBvdXRwdXQgYmV0d2VlbiB6ZXJvIGFuZCBvbmUsIHdlIHdvdWxkIGhhdmUgYSB2YWx1ZSB0aGF0IGNvdWxkIGJlIGFueXdoZXJlIGZyb20gemVybyB0byBvbmUsIGFuZCwgcXVpdGUgcG9zc2libHksIGNvdWxkIGdvIG92ZXIgb3IgdW5kZXIuDQoNClNvIHRoZSBxdWVzdGlvbiBiZWNvbWVzLCBob3cgY2FuIHdlIHVzZSB3aGF0IHdlJ3ZlIGxlYXJuZWQgaW4gbGluZWFyIHJlZ3Jlc3Npb24gdG8gdGFja2xlIHRoZXNlIHByb2JsZW1zIHdoZXJlIHRoZSBvdXRwdXQgaXMtLQ0KDQpvciB0aGUgcHJlZGljdGlvbi0tDQoNCmlzIGNhdGVnb3JpY2FsPyBTbyB3aGVuIHdlIGxvb2sgYXQgd2hhdCBoYXBwZW5zIHdpdGggYSBjb250aW51b3VzIG91dHB1dCB2ZXJzdXMgYSBjYXRlZ29yaWNhbCBvdXRwdXQsIHdoYXQgd2UgbmVlZCB0byBkbyBpcywgd2Ugd2lsbCBzZWUgb24gdGhlIGxlZnQgaGFuZCBzaWRlIHRoYXQgdGhlIG91dHB1dCB2YXJpZXMgd2l0aCB4LiBTbyB0aGVyZSdzIGEgY29udGludW91cyBvdXRwdXQuIFRoZSBkaXNjcmV0ZSBvdXRwdXQgbWVhbnMgdGhhdCB5IGNhbiBvbmx5IHRha2Ugb24gc3BlY2lmaWMgdmFsdWVzLg0KDQpJbiB0aGlzIGNhc2UsIGl0IHdvdWxkIGJlIHJvdWdobHkgbmVnYXRpdmUgb25lIG9yIG9uZS4gQW5kIGl0IGRvZXNuJ3QgaGF2ZSB0byBqdW1wIGV4YWN0bHkgYXQgemVyby4gSW4gdGhpcyBjYXNlLCBpdCBjYW4gdGFrZSBvbiBkaWZmZXJlbnQgZGlzY3JldGUgdmFsdWVzIGZvciBkaWZmZXJlbnQgcmFuZ2VzIG9mIHguIEl0IGRvZXNuJ3QgaGF2ZSB0byBiZSB4IGdyZWF0ZXIgdGhhbiAwLCB4IGxlc3MgdGhhbiB6ZXJvLg0KDQpJdCBjb3VsZCBiZSBzb21ldGhpbmcgbGlrZSB4IGdyZWF0ZXIgdGhhbiBuZWdhdGl2ZSBvbmUgb3IgeCBsZXNzIHRoYW4gdGhhbiBuZWdhdGl2ZSBvbmUuIEJ1dCB3ZSBzZWUgdGhhdCB5IG9ubHkgdGFrZXMgb24gZGlzY3JldGUgdmFsdWVzLiBTbyB3ZSB0YWxrZWQgYWJvdXQgZGlzY3JldGUgbnVtYmVycywgYnV0IHRoaXMgaXMgbm93IGEgZGlzY3JldGUgdGFyZ2V0LiBBbmQsIGluIHBhcnRpY3VsYXIsIHdlJ3JlIGdvaW5nIHRvIGxvb2sgYXQgYmluYXJ5IHRhcmdldHMgd2hlcmUgd2UndmUgZ290IHR3byB2YWx1ZXMuDQoNCk9uZSBvZiB3aGljaCB3aWxsIGJlIGEgdmFsdWUgemVybywgYW5kIG9uZSBvZiB3aGljaCB3aWxsIGJlIGEgdmFsdWUgb25lLCB3aGljaCB3aWxsIGNvcnJlc3BvbmQgdG8gb3VyIG9uZS1ob3QgZW5jb2RlZCB0YXJnZXRzLiBTbyB0byBkbyB0aGF0LCB3ZSdyZSBnb2luZyB0byBjb21lIGJhY2sgdG8gbGluZWFyIHJlZ3Jlc3Npb24uIEFuZCwgaG9wZWZ1bGx5LCB0aGVzZSBtYXRoZW1hdGljYWwgZXF1YXRpb25zIGxvb2sgZmFtaWxpYXIgdG8geW91LCBidXQgaXQncyBvdXIgb2xkIHkgZXF1YWxzIG14IHBsdXMgYi4gU28sIG9mIGNvdXJzZSwgZWFjaCBjb2x1bW4sIHgtLQ0KDQppbiB0aGlzIGNhc2UgeDEsIHgyLCBldCBjZXRlcmEtLQ0KDQpoYXMgYW4gYXNzb2NpYXRlZCBzbG9wZSwgbTEsIG0yLiBBbmQgYSByZW1pbmRlciB0aGF0IHRoZSBpbnRlcmNlcHQgaXMgaGlkaW5nIGFzIG0gemVyby4gU28gaWYgeW91J3JlIHdvbmRlcmluZywgd2h5IGlzIHRoaXMgdGhlIHN1bSBvZiBtaSB4aSwgeDAgaXMgYWN0dWFsbHkgb25lLCBhbmQgbTAsIG9mIGNvdXJzZSwgd2lsbCB0aGVuIGJlIHRoZSBpbnRlcmNlcHQuIEFuZCB0aGlzIGlzIHRoZSBjb21wYWN0IG5vdGF0aW9uIEkgbWVudGlvbmVkIGEgZmV3IHZpZGVvcyBhZ28uDQoNClNvIHdoYXQgd2UncmUgZ29pbmcgdG8gZG8gaXMsIHdlJ3JlIGdvaW5nIHRvIHRha2UgYSB2YXJpYWJsZSB0cmFuc2Zvcm0uIEFuZCB0aGlzIHZhcmlhYmxlIHRyYW5zZm9ybSBpcyBnb2luZyB0byB0YWtlIHVzIHRvIGxvZ2lzdGljIHJlZ3Jlc3Npb24uIE5vdywgaXQgc2VlbXMgbGlrZSB0aGlzIGlzIGEgc3RyYW5nZSB0eXBlIG9mIHZhcmlhYmxlIHRyYW5zZm9ybS4gQnV0LCBhcyB3ZSdyZSBnb2luZyB0byBzZWUsIHRoaXMgYWN0dWFsbHkgZm9yY2VzIG91ciBvdXRwdXQgdG8gbm93IGVzc2VudGlhbGx5IGJlIGRpc2NyZXRlLg0KDQpXZSdyZSBnb2luZyB0byBtb2RlbCBhIGRpc2NyZXRlIG91dHB1dCwgYW5kIHRoYXQncyB3aGF0IHRoaXMgZnVuY3Rpb24gaXMgZ2l2aW5nIHVzLiBBbmQgdGhpcyBmdW5jdGlvbiB3aWxsIGxlYWQgdXMgdG8gbG9naXN0aWMgcmVncmVzc2lvbi4gTm93LCBvbmUgb2YgdGhlIHRoaW5ncyB5b3UnbGwgbm90aWNlIGlzIEkgYWRkIHRoaXMgc2lnbWEgYXQgdGhlIGJvdHRvbSwgc2lnbWEgb2YgbWkgeGkuIEFuZCB0aGF0IHNpbXBseSBtZWFucyB0aGF0IHRoZSBzaWdtYSBmdW5jdGlvbiwgb3Igd2hhdCB3ZSdyZSBnb2luZyB0byBjYWxsIHRoZSBzaWdtb2lkIGZ1bmN0aW9uLCB0YWtlcyBhcyBpdHMgaW5wdXQsIG0gdGltZXMgeC0tDQoNCnRoZSBzdW1tYXJ5IG9mIHRoYXQuIEFuZCwgdHlwaWNhbGx5LCB3ZSdsbCBkcm9wIHRoZSBzdW1tYXRpb24uIEl0J3MgaW1wbGllZCBiZWNhdXNlIHdlIGtub3cgZm9yIGxpbmVhciByZWdyZXNzaW9uIHRoYXQgd2Ugc2VlIHRoaXMgb3JpZ2luYWwgdG9wIGVxdWF0aW9uLS0NCg0KdGhlIG0xIHgxIHBsdXMgbTIgeDIsIGV0IGNldGVyYSwgZXQgY2V0ZXJhLiBCZWNhdXNlIHdlJ3JlIHNvIGZhbWlsaWFyIHdpdGggdGhhdCwgZm9yIHNob3J0bmVzcyBhbmQgYnJldml0eSB3ZSB0eXBpY2FsbHkgZHJvcCB0aGUgc3VtbWF0aW9uIHNpZ24uIEJ1dCBpdCdzIGFscmVhZHkgdGhlcmUuIEFuZCBzbyBJJ3ZlIGRyb3BwZWQgaXQgaGVyZSBmb3IgY2xhcml0eSwgYnV0IHlvdSBjYW4gc2VlIHRoYXQgeSBpcyBub3cgYSBmdW5jdGlvbiBvZiBtIHRpbWVzIHggcmF0aGVyIHRoYW4gbSB0aW1lcyB4IGRpcmVjdGx5Lg0KDQozLiB0aGUgc2lnbW9pZA0KDQpIaSwgYW5kIHdlbGNvbWUgdG8gdGhlIHZpZGVvIGFib3V0IHRoZSBzaWdtb2lkIGZ1bmN0aW9uLCB3aGljaCBpcyBvbmUgb2YgdGhlIGtleSBmdW5jdGlvbnMgaW4gdHJhbnNmb3JtaW5nIGZyb20gYSBsaW5lYXIgcmVncmVzc2lvbiB0byBhIGxvZ2lzdGljIHJlZ3Jlc3Npb24uIFNvIGluIHRoZSBsYXN0IHZpZGVvLCB3ZSB0YWxrZWQgYSBsaXR0bGUgYml0IGFib3V0IHRoaXMgdmFyaWFibGUgdHJhbnNmb3JtLiBOb3csIHdlIGNhbiBzdGFydCB0byBzZWUgd2hhdCB0aGlzIHZhcmlhYmxlIHRyYW5zZm9ybSBhY3R1YWxseSBkb2VzIHRvIG91ciBkYXRhLiBTbyB0aGlzIHNpZ21vaWQgZnVuY3Rpb24gdGFrZXMgdGhlIGlucHV0IHgsIGFuZCBpdCBlc3NlbnRpYWxseSBzcXVlZXplcyB0aGUgb3V0cHV0IHNvIHRoYXQgaXQncyBiZXR3ZWVuIDAgYW5kIDEuDQoNCkFuZCBpdCBnaXZlcyB1cyB0aGUgUy1zaGFwZWQgY3VydmUgdGhhdCB3ZSBzZWUgb24gdGhlIGxlZnQuIEhvd2V2ZXIsIGFzIHRoYXQgc2xvcGUtLQ0KDQphbmQgd2hlbiBJIHNheSBzbG9wZSwgdGhpcyB3b3VsZCBiZSB0aGUgc2xvcGVzIG0tLQ0KDQpnZXQgbGFyZ2VyIGFuZCBsYXJnZXIsIHdlIHNlZSB0aGF0IHRoaXMgc2lnbW9pZCBmdW5jdGlvbiBiZWNvbWVzIG11Y2ggbW9yZSBsaWtlIGEgc3RlcCBmdW5jdGlvbiwgbW9kZWxpbmcgdGhlIGJlaGF2aW9yIHRoYXQgd2Ugc2F3IGZvciBkaXNjcmV0ZSBvdXRwdXQuIEFuZCB0aGlzIHNob3dzIHVzIHRoYXQgd2UgY2FuIGFjdHVhbGx5IGdldCBhbiBvdXRwdXQgdGhhdCBpcyBlc3NlbnRpYWxseSBmb3JjZWQgdG8gYmUgZWl0aGVyIDAgb3IgMS4gTm93LCB3ZSdsbCBzdGlsbCBnZXQgb3V0cHV0cyB0aGF0IGFyZSBiZXR3ZWVuIDAgYW5kIDEuIFNvIHNvbWUgcGVvcGxlIG1heSBzYXksIHdlbGwsIGFsdGhvdWdoIHdlJ3ZlIHNxdWVlemVkIHRoZSB0YWlscyB0byAwIGFuZCAxLCB3aGF0IGFib3V0IHRoZSBwb2ludCBpbiB0aGUgbWlkZGxlPw0KDQpBbmQgd2hhdCB3ZSdsbCBsb29rIGF0LCBhdCB0aGVzZSBwb2ludHMgaW4gdGhlIG1pZGRsZSwgaXMgdGhlc2UgYXJlIGdvaW5nIHRvIHJlcHJlc2VudCBwcm9iYWJpbGl0aWVzIHRoYXQgd2UgYmVsb25nIHRvIGNsYXNzIDEgb3IgY2xhc3MgMC4gU28gd2hhdCB3ZSdyZSBsb29raW5nIGF0IG5vdyBpcyB3ZSB3aWxsIGZpbmQgYSB0aHJlc2hvbGQgb3IgYSBjdXRvZmYgaW4gb3VyIHNpZ21vaWQgZnVuY3Rpb24uIEFuZCB3ZSdsbCBzYXkgYW55dGhpbmcgYWJvdmUgdGhpcyB2YWx1ZSBpcyBjbGFzcyAxLiBBbmQgYW55dGhpbmcgYmVsb3cgaXQgaXMgY2xhc3MgMC4NCg0KVGhlIGRlZmF1bHQsIG9mIGNvdXJzZSwgd291bGQgYmUgYXQgeSBlcXVhbHMgMC41LCB0aGUgaWRlYSBiZWluZyBlc3NlbnRpYWxseSByb3VuZGluZy4gU28gd2UgY291bGQgdGFrZS0tDQoNCmlmIGl0cyAwLjUxIGFuZCBhYm92ZSwgaXQgYmVsb25ncyB0byBjbGFzcyAxLiBBbmQgaWYgaXQgYmVsb25ncyB0byAwLjUwIGFuZCBiZWxvdywgaXQgYmVsb25ncyB0byBjbGFzcyAwIGxpa2Ugb3VyIG5vcm1hbCByb3VuZGluZyB3b3VsZCBiZS4NCg0KSG93ZXZlciwgdGhpcyBpcyBub3QgcmVxdWlyZWQuIFlvdSBjYW4gbWFrZSB5b3VyIHRocmVzaG9sZHMgd2hlcmV2ZXIgeW91IHdhbnQuIEFuZCB0aGlzIGlzIGltcG9ydGFudCBhcyB3ZSBsb29rIGZvcndhcmQuIEFuZCB3ZSBtYXliZSB3YW50IHRvIGJlIGNhdXRpb3VzIGFib3V0IGNlcnRhaW4gYXNzaWdubWVudHMuDQoNClNvIHdoYXQgaWYgYW4gYXNzaWdubWVudCBmb3IgMSBpcyBhIGZyYXVkIGRldGVjdGlvbj8gV2VsbCwgd2UgbWlnaHQgd2FudCB0byBoYXZlIGVpdGhlciBhIHZlcnkgbG93IHRocmVzaG9sZC4gTGlrZSB3ZSB3YW50IHRvIGRldGVjdCBhbnkgcG9zc2libGUgY2hhbmNlIG9mIGZyYXVkLiBTbyBwZXJoYXBzIHdoZW4gb3VyIHkgb3V0cHV0IHZhbHVlIGlzIHNheSAwLjIsDQoNCnNvIHRoYXQgd291bGQgbWVhbiB0aGF0IHRoZXJlJ3MgYWJvdXQgYSAyMCUgY2hhbmNlIHRoYXQgdGhlcmUncyBmcmF1ZCBnb2luZyBvbi4gU28gbWF5YmUgd2Ugc2hvdWxkIGNoZWNrIHRoYXQuIE9uIHRoZSBvdGhlciBoYW5kLCBpZiB5b3UgcHV0IHRoZSB0aHJlc2hvbGQgcmVhbGx5IGhpZ2gsIHlvdSB3b3VsZCBzYXksIGFoLCBJJ20gZXh0cmVtZWx5IGNlcnRhaW4gdGhhdCBmcmF1ZCBpcyBnb2luZyBvbiBhdCBzb21ldGhpbmcgbGlrZSAwLjk5LiBCdXQgdGhlIHBvaW50IGlzIHlvdSBoYXZlIHRoZSBjaG9pY2UgdG8gYWRqdXN0IHRoYXQgdGhyZXNob2xkLg0KDQpTbyB0aGUgZGVmYXVsdCBvdXRwdXQgaWYgeW91IGRvbid0IGluc3RydWN0IHlvdXIgYWxnb3JpdGhtIHdpbGwgbWFrZSB0aGUgY3V0b2ZmIGF0IDAuNS4gVGhpcyBpcyBvbmUgb2YgdGhlIG1vc3QgY29tbW9uIGVycm9ycyBJIHNlZSBzdHVkZW50cyBtYWtlIGlzIHRoZXkgZG9uJ3QgcXVlc3Rpb24gd2hlcmUgdGhhdCB0aHJlc2hvbGQgc2hvdWxkIGJlLiBOb3csIHRoZXJlIGFyZSBhIG51bWJlciBvZiB3YXlzIHRoYXQgd2UgY2FuIGRldGVybWluZSB0aGF0IHRocmVzaG9sZC4gSXQncyBub3QgZW50aXJlbHkgYSBqdWRnbWVudCBjYWxsLg0KDQpCdXQgYmUgYXdhcmUgdGhhdCB5b3UgY2FuIG1ha2UgdGhlIGRldGVybWluYXRpb24gd2hlcmUgc2hvdWxkIHlvdXIgYXNzaWdubWVudCBiZSBmb3IgY2xhc3MgMSBhbmQgY2xhc3MgMC4gU28gbGV0J3MgdGFrZSBhIGxvb2sgYXQgdGhlIHByb3BlcnRpZXMgb2YgdGhlIHNpZ21vaWQgZnVuY3Rpb24uIFdlIGNhbiBzZWUgdGhhdCB3ZSBoYXZlIHRoZSBvdXRwdXQgeSBpcyAxIG92ZXIgMSBwbHVzIGFuIGV4cG9uZW50aWFsLiBBbmQgdGhhdCBleHBvbmVudGlhbCwgYWdhaW4sIHRoZSBpbXBsaWVkIHN1bSwgd2UgaGF2ZSBoZXJlIG14Lg0KDQpCdXQgaXQncyBhY3R1YWxseSBhIHN1bW1hcnkgb2YgYWxsIG0ncyBhbmQgYWxsIHRoZSB4J3MuIFNvLCBhZ2FpbiwgbTAgdGltZXMgeDAsIHgwIGJlaW5nIDEsIG0wIGJlaW5nIHRoZSBpbnRlcmNlcHQsIHgxIG0xLCBvdXIgZmlyc3QgY29sdW1uIGFuZCBmaXJzdCBzbG9wZS4gQnV0IHRoZSBpZGVhIGlzIGlmIHRoYXQgc3VtIHRvdGFsIGlzIGV4dHJlbWVseSBsYXJnZSBhbmQgaXQncyBwb3NpdGl2ZSwgd2UnbGwgaGF2ZSAxIG92ZXIgMSBwbHVzIGUgdG8gdGhlIG5lZ2F0aXZlIGxhcmdlIG51bWJlci4gZSB0byBhIG5lZ2F0aXZlIGxhcmdlIG51bWJlciBpcywgaW4gZXNzZW5jZSwgYSB2ZXJ5LCB2ZXJ5IHNtYWxsIG51bWJlciBiZWNhdXNlIGl0J3MgMSBvdmVyIGUgdG8gdGhhdCBwb3dlci4NCg0KU28gdGhhdCBudW1iZXIgYmVjb21lcyAxIHBsdXMgYW4gZXh0cmVtZWx5IHNtYWxsIG51bWJlci4gVGhhdCBiZWNvbWVzIDEgb3ZlciAxLCB3aGljaCBtZWFucyBvdXIgb3V0cHV0IGlzIDEuIElmIG91ciBtIHRpbWVzIHggaXMgbmVnYXRpdmUtLQ0KDQpub3csIHJlbWVtYmVyLCB3ZSBoYXZlIGEgbmVnYXRpdmUgc2lnbiBpbiBmcm9udCBvZiBtIHRpbWVzIHguIFRoYXQgbWVhbnMgbSB0aW1lcyB4IGl0c2VsZiBpcyBuZWdhdGl2ZS4gVGhlbiB3ZSBoYXZlIGFuIGUgdG8gdGhlIHBvc2l0aXZlIG51bWJlciBiZWNhdXNlIHRoZSB0d28gbmVnYXRpdmUgc2lnbnMgd2lsbCBjYW5jZWwgb3V0LiBOb3csIEkndmUgZ290IGFuIGV4cG9uZW50aWFsIHRvIGEgdmVyeSBsYXJnZSBwb3dlci4NCg0KV2VsbCwgYW55dGltZSBJIHRha2Ugc29tZXRoaW5nIHRvIGEgbGFyZ2UgcG93ZXIsIHRoYXQgbnVtYmVyIGdldHMgdmVyeSwgdmVyeSBsYXJnZS4gU28gSSBoYXZlIDEgb3ZlciAxIHBsdXMgYSBsYXJnZSBudW1iZXIuIDEgcGx1cyBhIGxhcmdlIG51bWJlciBpcyBlc3NlbnRpYWxseSB0aGF0IGxhcmdlIG51bWJlci4gQW5kIDEgb3ZlciBhIGxhcmdlIG51bWJlciBpcyAwIHJvdWdobHkuDQoNCk5vdywgYWdhaW4sIEkgbGlrZSB0byB0YWxrIGluIHBoeXNpY3MgdGVybXMgYmVjYXVzZSBvZiBteSBwaHlzaWNzIGJhY2tncm91bmQuIEJ1dCB0aGUgaWRlYSBpcyB0aGF0IHdlIGFyZSBleHRyZW1lbHkgY2xvc2UgdG8gMCB3aXRoIDEgb3ZlciBhIGxhcmdlIG51bWJlci4gQW5kIHRoYXQncyB0aGUgYmVoYXZpb3Igd2UgYWN0dWFsbHkgd2FudC4gV2Ugd2FudCBpZiB0aGUgb3V0cHV0IGlzIGxhcmdlIHRvIGJlLCB5ZXMsIHRoaXMgaXMgY2xhc3MgMS4NCg0KQW5kIHRoYXQncyBob3cgd2UncmUgZ29pbmcgdG8gbW9kZWwgaXQuIFlvdSBtYXkgYXNrIHlvdXJzZWxmLCB3aHkgZGlkIHdlIHNheSBpZiBteCBpcyBsYXJnZSwgd2Ugd2FudCBpdCB0byBiZSBvbmUsIGFuZCBpZiBhbiB4IGlzIG5lZ2F0aXZlLCB3ZSB3YW50IGl0IHRvIGJlIDA/IFdlbGwsIHRoYXQncyB0aGUgaWRlYSBvZiB1cyB0YWtpbmcgYWR2YW50YWdlIG9mIHRoaXMgc2lnbW9pZCBwcm9ibGVtLiBTbyB3ZSB3aWxsIGZyYW1lIHRoZSBwcm9ibGVtIHN1Y2ggdGhhdCB3ZSB3YW50IHRoZSBvdXRwdXQgdG8gYmUgbGFyZ2UgZm9yIGNsYXNzIDEsIGFuZCBsYXJnZSBhbmQgcG9zaXRpdmUsIGFuZCBuZWdhdGl2ZSBmb3IgY2xhc3MgMC4NCg0KVGhpcyBzaW11bGF0ZXMgdGhlIG91dHB1dCBvZiB0aGUgc3RlcCBmdW5jdGlvbi4gQW5kIGFzIGFuIGFkZGVkIGJvbnVzLCBhbHRob3VnaCBtYW55IHBlb3BsZSBhcmUgaW50aW1pZGF0ZWQgYnkgdGhlIG1hdGhlbWF0aWNzIG9mIGV4cG9uZW50aWFscywgZXhwb25lbnRpYWxzIGFjdHVhbGx5IGhhdmUgc29tZSB2ZXJ5IGZyaWVuZGx5IHByb3BlcnRpZXMgdGhhdCB3ZSdyZSBnb2luZyB0byB0YWtlIGFkdmFudGFnZSBvZi4gQW5kIHRoZXknbGwgaGVscCB1cyB3aXRoIGRvaW5nIHNvbWUgYWR2YW5jZWQgbWF0aGVtYXRpY3MgaW4gdGhlIG5leHQgY291cGxlIG9mIHN0ZXBzLiBTbyB3ZSdsbCB1c2UgdGhpcyBzaWdtb2lkIGZ1bmN0aW9uIHRvIGdpdmUgdXMgYW4gYWR2YW50YWdlLg0KDQpBbmQsIGhvcGVmdWxseSwgd2UnbGwgc2VlIHRoYXQgaW4gdGhlIG5leHQgY291cGxlIG9mIHZpZGVvcy4gQW5kIGl0IHdvbid0IGJlIHNvIG9ic2N1cmUgd2h5IGRpZCB3ZSBjaG9vc2UgdGhpcyBwYXJ0aWN1bGFyIGZ1bmN0aW9uLiBBdCBpdHMgZmFjZSB2YWx1ZSwgd2UncmUgbG9va2luZyBhdCwgb2YgY291cnNlLCBvdXRwdXRzIHRoYXQgYXJlIDAgYW5kIDEuIEFuZCB0aGlzIGZ1bmN0aW9uIHN0YXJ0cyB0byBzaW11bGF0ZSB0aGF0Lg0KDQpBbmQgdGhlbiB3ZSdyZSBnb2luZyB0byB0YWtlIGFkdmFudGFnZSBvZiB0aGF0IGV4cG9uZW50aWFsIHByb3BlcnR5IGFzIHdlIGdvIGZvcndhcmQuDQoNCg0KNC4gbG9nIGxvc3MNCkhpLCBhbmQgd2VsY29tZSB0byBvdXIgdmlkZW8gb24gbG9nYXJpdGhtaWMgbG9zcy4gU28sIGluIGxpbmVhciByZWdyZXNzaW9uLCB3aGF0IHdlIGZpbmQgaXMgdGhhdCB3ZSB0eXBpY2FsbHkgdXNlIHRoZSBtZWFuIHNxdWFyZWQgZXJyb3Igb3IgdGhlIG1lYW4gYWJzb2x1dGUgZXJyb3IuIEFuZCB0aGUgaWRlYSBpcywgd2UgYXJlIG1lYXN1cmluZyB0aGUgZGlzdGFuY2UgYmV0d2VlbiBvdXIgcHJlZGljdGlvbiBhbmQgdGhlIHRhcmdldC4gQW5kIGJlY2F1c2UgZGlzdGFuY2UsIG9mIGNvdXJzZSwgaXMgUHl0aGFnb3JlYW4gdGhlb3JlbSBiYXNlZCwgd2UgdHlwaWNhbGx5IHVzZSB0aGUgc3F1YXJlLg0KDQpIb3dldmVyLCBiZWNhdXNlIHdlJ3JlIGFsc28gb25lIGRpbWVuc2lvbmFsIHdpdGggcHJlZGljdGlvbiBhbmQgdGFyZ2V0LCBpdCdzIGFsc28gcG9zc2libGUgdG8gdXNlIHRoZSBtZWFuIGFic29sdXRlIGVycm9yLiBBbmQgYWxsIHdlJ3JlIGRvaW5nIGlzIGp1c3QgbWVhc3VyaW5nIGhvdyBmYXIgYXdheSBmcm9tIHRoZSBhY3R1YWwgdmFsdWUgaXMgb3VyIGd1ZXNzLiBBbmQgc28gb3VyIGxvc3MgaXMgYWN0dWFsbHkgdGhlIHN1bSBvZiB0aGUgZGlzdGFuY2VzIGZyb20gdGhlIHByZWRpY3Rpb24gaW4gdGhlIHZhbHVlIG9mIHggdG8gdGhlIHRhcmdldCBpbiB5LiBBbmQgdGhhdCdzIHdoeSB3ZSB1c2UgdGhlIHZlcnRpY2FsIGRpc3RhbmNlIHJhdGhlciB0aGFuIHRoZSBzaG9ydGVzdCBkaXN0YW5jZS4NCg0KVGhlIGlkZWEgaXMgdGhhdCB0aGUgaW5wdXQgYW5kIG91ciBkYXRhIGlzIHVzZWQgYW5kIGZpeGVkIGZvciB1cy4gQW5kIHNvIHdlIGFkanVzdCB0aGUgc2xvcGUgb2YgdGhlIGxpbmUgdG8gcHJvdmlkZSBhIHByZWRpY3Rpb24uIEFuZCB0aGUgZGlzdGFuY2UgZnJvbSB0aGUgcG9zaXRpb24gYXQgaW5wdXQgeCB0byB0aGUgdGFyZ2V0IHkgaXMgdGhlbiBvdXIgdmVydGljYWwgZGlzdGFuY2UuIFNvIG91ciBvYmplY3QgaXMgdG8gZ2V0IG91ciBzdHJhaWdodCBsaW5lIGFzIGNsb3NlIHRvIHRoZSB0YXJnZXQgcG9pbnRzIGFzIHBvc3NpYmxlLCBtaW5pbWl6aW5nIGFsbCBvZiB0aG9zZSBkaXN0YW5jZS4NCg0KVGhhdCdzIHdoYXQgd2UncmUgZG9pbmcgd2hlbiB3ZSdyZSB1c2luZyBib3RoIG1lYW4gc3F1YXJlZCBlcnJvciBhbmQgbWVhbiBhYnNvbHV0ZSBlcnJvci4gSG93ZXZlciwgd2hlbiB3ZSBnZXQgdG8gbG9nYXJpdGhtaWMgbG9zcyBhbmQgY2F0ZWdvcmljYWwgZGF0YSwgd2UndmUgZ290IGEgcHJvYmxlbS4gSGVyZSwgb3VyIG91dHB1dCBvciBvdXIgdGFyZ2V0LCBvdXIgZ3Vlc3MsIHJlYWxseSBoYXMgb25seSB0d28gdmFsdWVzLiBUaG9zZSB2YWx1ZXMgdGVuZCB0byBiZSB6ZXJvIGFuZCBvbmUuDQoNClNvIHdoYXQgd2UgbmVlZCBpcywgd2UgbmVlZCBhIGxvc3MgZnVuY3Rpb24gdGhhdCByZXByZXNlbnRzIG91ciBkaXN0YW5jZSB0byB0aGlzIHRhcmdldCBhbmQgb3VyIGVycm9yIHRoYXQgd2hlbiB3ZSBndWVzcywgaXMgaXQgdGFyZ2V0IHplcm8gb3IgaXMgaXQgdGFyZ2V0IG9uZSwgaG93IGZhciBhd2F5IGZyb20gdGhhdCBndWVzcyBhcmUgd2U/IFNvIHdlJ3JlIGFjdHVhbGx5IGdvaW5nIHRvIGhhdmUgdHdvIGNhc2VzIGJlY2F1c2Ugd2UgaGF2ZSB0aGUgY2FzZSB3aGVyZSBvdXIgdGFyZ2V0IGlzIHplcm8gYW5kIHRoZSBjYXNlIHdoZXJlIG91ciB0YXJnZXQgaXMgb25lLiBTbyBpZiB0aGUgdGFyZ2V0IGlzIG9uZSwgdGhlIGVycm9yIGlzIGp1c3QgZ29pbmcgdG8gYmUgbWludXMgeSBsb2cgd2l0aCBwLiBBbmQgcCBpcyBqdXN0IG91ciBvdXRwdXQgb2Ygb3VyIHNpZ21vaWQgZnVuY3Rpb24uDQoNCkkndmUgbWVudGlvbmVkLCBpZiB3ZSd2ZSBiZWVuIGRlYWxpbmcgd2l0aCBzaWdtb2lkIGZ1bmN0aW9ucywgd2Uga25vdyB0aGF0IHRoZSBvdXRwdXQgb2YgdGhlIHNpZ21vaWQgZnVuY3Rpb24gcmVwcmVzZW50cyBhIHByb2JhYmlsaXR5IHRoYXQgdGhhdCBpbnB1dCBiZWxvbmdzIHRvIGEgY2VydGFpbiBjbGFzcy4gU28gcCBpcyByZWFsbHkganVzdCB0aGUgb3V0cHV0IG9yIHRoZSBzaWdtb2lkIG9mIG0gdGltZXMgeC4gSXQncyB0aGF0IG9uZSBvdmVyIHRoZSBleHBvbmVudGlhbC4gU28gd2hhdCB3ZSdyZSBkb2luZyBpcywgd2UncmUgbG9va2luZyBhdCB0aGUgZGlzdGFuY2UgYmV0d2VlbiBvdXIgdGFyZ2V0IGFuZCBvdXIgZ3Vlc3Mgb3Igb3VyIG91dHB1dC4NCg0KQW5kIHdlIHB1dCB5IGluIGZyb250IG9mIGl0IGJlY2F1c2Ugd2UncmUgZ29pbmcgdG8gY29tYmluZSB0aGVzZSB0d28gY2FzZXMuIE5vdywgaW4gdGhpcyBjYXNlLCB0aGUgdGFyZ2V0IHkgaXMgb25lLCBzbyBvdXIgZXJyb3IgaXMgcmVhbGx5IGp1c3QgbWludXMgbG9nIHAuIE5vdywgaGVyZSwgSSdtIHVzaW5nIHRoZSB0cmFkaXRpb25hbCBub3RhdGlvbiB1c2VkIGluIGRhdGEgc2NpZW5jZS4gSW4gdGhpcyBjYXNlLCB0aGUgbG9nIGlzIG5vdCBsb2cgYmFzZSAxMCwgaXQncyBhY3R1YWxseSB0aGUgbmF0dXJhbCBsb2cuDQoNClRoYXQgd2lsbCBiZWNvbWUgaW1wb3J0YW50IGluIGEgZmV3IG1vbWVudHMuIE5vdywgd2hlbiB0aGUgdGFyZ2V0IGlzIHplcm8sIHdlIHNlZSB3ZSBoYXZlIGEgb25lIG1pbnVzIHkuIEluIG90aGVyIHdvcmRzLCBvbmUgbWludXMgemVybyB0aW1lcyB0aGUgbG9nIG9mIG9uZSBtaW51cyB0aGUgcHJvYmFiaWxpdHkuIFRoaXMgaXMgYmFzaWNhbGx5IGFuIGV4Y2x1c2lvbi4NCg0KU28sICwganVzdCBsaWtlIGJlZm9yZSwgd2Ugc2FpZCwgd2UgaGFkIGEgcHJvYmFiaWxpdHkgdG8gYmVsb25nIHRvIGNsYXNzIG9uZS4gV2VsbCwgaWYgd2UndmUgZ290IGEgY2xhc3Mgb25lIGFuZCBhIGNsYXNzIHplcm8sIGFuZCB3ZSBsb29rIGF0IHRoaXMgYXMsIHdoYXQncyB0aGUgcHJvYmFiaWxpdHkgSSBiZWxvbmcgdG8gY2xhc3Mgb25lPyBCdXQgdGhleSdyZSBtdXR1YWxseSBleGNsdXNpdmUuIFNvIHRoYXQgbWVhbnMgdGhhdCB0aGUgcHJvYmFiaWxpdHkgSSBiZWxvbmcgdG8gY2xhc3MgemVybyBpcyBvbmUgbWludXMgdGhlIHByb2JhYmlsaXR5IG9mIGJlbG9uZ2luZyB0byBjbGFzcyBvbmUuDQoNCkl0J3Mgc2ltcGx5IGZpbmRpbmcgdGhlIHByb2JhYmlsaXR5IHRoYXQgSSBiZWxvbmcgdG8gdGhlIG90aGVyIGNsYXNzLiBCdXQgdGhlc2UgdHdvIGNsYXNzZXMgYXJlIGNvdXBsZWQsIGFuZCBzbyB3ZSBzZWUgdGhlIG9uZSBtaW51cyBwLiBTbywgYWdhaW4sIHAgaXMgb3VyIHByZWRpY3Rpb24gb3Igb3V0cHV0IG9mIG91ciBzaWdtb2lkLiBTbyBpZiB3ZSBwdXQgdGhlc2UgdHdvIHRlcm1zIHRvZ2V0aGVyLCB3ZSBnZXQgYSB0d28tdGVybSBsb3NzIGZ1bmN0aW9uLg0KDQpCdXQgeW91IHNlZSB0aGF0IG9uZSBvZiB0aG9zZSB0ZXJtcyBpcyBhbHdheXMgemVyby4gSWYgeSBpcyBvbmUsIHRoZSByaWdodC1oYW5kIHRlcm0gYmVjb21lcyBvbmUgbWludXMgb25lIG9yIHplcm8sIHNvIHRoYXQgdGVybSBjb250cmlidXRlcyBub3RoaW5nIHRvIHRoZSBsb3NzLiBPbiB0aGUgb3RoZXIgaGFuZCwgaWYgeSBpcyB6ZXJvLCB0aGUgbGVmdC1oYW5kIHRlcm0gYmVjb21lcyB6ZXJvLiBBbmQgd2UgaGF2ZSwgb25jZSBhZ2FpbiwgbWludXMgbG9nIG9mIG9uZSBtaW51cyBwLg0KDQpBbmQgdGhhdCdzIHRoZSBpZGVhIG9mLCB3aGF0J3MgbXkgcHJvYmFiaWxpdHkgb2YgYmVsb25naW5nIHRvIGNsYXNzIHplcm8/IFNvIGJvdGggdGVybXMgYXJlIHN5bW1ldHJpYy4gV2hhdCB3ZSdyZSBzZWVpbmcgaXMgd2UgYXJlIGp1c3QgdGFraW5nIGFkdmFudGFnZSBvZiBvdXIgY2xhc3NlcyBiZWluZyBjb3VwbGVkIHRvIHNlbGVjdCB3aGljaCB0ZXJtIHdlJ3JlIGdvaW5nIHRvIHVzZS4gU28sIGhlcmUsIHdoYXQgd2UncmUgZG9pbmcgaXMsIHdlIGFyZSBwZW5hbGl6aW5nIHByZWRpY3Rpb25zIHRoYXQgYXJlIGZhcnRoZXIgYXdheS4NCg0KU28gaWYgSSdtIHRyeWluZyB0byBwcmVkaWN0IHplcm8gYW5kIG15IG91dHB1dCBpcyByZWFsbHksIHJlYWxseSBsYXJnZSwgbG9nIG9mIG9uZSBtaW51cyB0aGUgcHJvYmFiaWxpdHkgaXMgZ29pbmcgdG8gYmUgYSBoaWdoIGNvbnRyaWJ1dGlvbiB0byB0aGUgbG9zcy4gT24gdGhlIG90aGVyIGhhbmQsIGlmIG15IHByZWRpY3Rpb24tLQ0KDQpJIHdhbnQgaXQgdG8gYmUgY2xvc2UgdG8gb25lLS0NCg0KbXkgdGFyZ2V0IGlzIG9uZSwgdGhlbiB0aGUgb3V0cHV0IHNob3VsZCBiZSBjbG9zZSB0byBvbmUuIEFuZCB0aGF0IG1lYW5zIG15IHByb2JhYmlsaXR5IHNob3VsZCBiZSBjbG9zZSB0byBvbmUuIEFzIHRoZSBmYXJ0aGVyIGFuZCBmYXJ0aGVyIEkgZ2V0IGF3YXkgZnJvbSBvbmUtLQ0KDQphbmQsIHJlbWVtYmVyLCB0aGUgb25seSB3YXkgdG8gZ2V0IGF3YXkgZnJvbSBvbmUgaXMgdG8gbW92ZSB0b3dhcmRzIHplcm8uIEJ5IHVzaW5nIHRoYXQgbWludXMgbG9nIHAgdGVybSwgSSdtIGFjdHVhbGx5IGluY3JlYXNpbmcgdGhlIHZhbHVlIG9mIG15IGVycm9yLiBTbyB0aGUgbW9yZSBJIHByZWRpY3QgemVyby0tDQoNCm9yIHRoZSBjbG9zZXIgSSBhbSB0byBwcmVkaWN0aW5nIHplcm8sIHRoZSBtb3JlIEkgY29udHJpYnV0ZSB0byB0aGUgbG9zcy4gSW4gb3RoZXIgd29yZHMsIG15IGVycm9yIGlzIGxhcmdlci4gU28sIHNvbWV0aW1lcywgaXQgaGVscHMgdXMgdG8gdGFrZSBhIGxvb2sgYXQgdGhpcy4gQW5kIGxldCdzIHRha2UgYSBsb29rIGF0IHRoYXQgdmlzdWFsbHkgbm93Lg0KDQpTbywgaGVyZSwgd2UgY2FuIHNlZSB3aGF0IGxvZyBsb3NzIGxvb2tzIGxpa2UsIGFuZCB3ZSBoYXZlIHRoZSBpbmRpdmlkdWFsIHRlcm1zLiBXZSBjYW4gc2VlIHRoZSBibHVlIGxpbmUsIHdoaWNoIGlzIHRoZSBwcm9iYWJpbGl0eSBmb3IgeSBlcXVhbHMgb25lLS0NCg0Kb3IgSSBzaG91bGQgc2F5IHRoZSBsb3NzIGZvciB5IGVxdWFscyBvbmUtLQ0KDQp5b3UnbGwgc2VlLCBpZiB3ZSdyZSBwcmVkaWN0aW5nIGNsb3NlIHRvIG9uZSwgb3VyIGxvc3MgaXMgdmVyeSwgdmVyeSBzbWFsbC4gQnV0IGlmIHdlJ3JlIHByZWRpY3RpbmcgY2xvc2UgdG8gemVybyBhbmQgb3VyIHRhcmdldCBpcyBvbmUsIHdlIGNvbnRyaWJ1dGUgYSBncmVhdCBhbW91bnQgdG8gdGhlIGxvc3MuIEFuZCB0aGUgaWRlYSBpcywgd2hlbiB3ZSdyZSB0cnlpbmcgdG8gbWluaW1pemUgdGhpcyBsb3NzIGVxdWF0aW9uLCB3aGF0IHdlJ3JlIGdvaW5nIHRvIHRyeSBhbmQgZG8gaXMsIGZvcmNlIG91ciBwcmVkaWN0aW9ucyB0byBiZSBjb3JyZWN0LiBJbiBvdGhlciB3b3JkcywgdGhlIG1vZGVsIHNob3VsZCBzdGFydCBvdXRwdXR0aW5nIHByZWRpY3Rpb25zIHRoYXQgYXJlIGNsb3NlciB0byBvbmUgYmVjYXVzZSB0aGV5IHdpbGwgY29udHJpYnV0ZSBsZXNzIHRvIHRoZSBsb3NzLg0KDQpMaWtld2lzZSwgd2hlbiB3ZSd2ZSBnb3QgYSB0YXJnZXQgdGhhdCdzIHplcm8sIHRoZSBmYXJ0aGVyIGF3YXkgZnJvbSB6ZXJvIHlvdSBhcmUsIHRoZSBtb3JlIHlvdSBjb250cmlidXRlIHRvIHRoZSBsb3NzLiBOb3csIHJlbWVtYmVyLCBvdXIgc2lnbW9pZCBpcyBnb2luZyB0byBzcXVhc2ggb3VyIG91dHB1dHMgdG8gYmUgYmV0d2VlbiB6ZXJvIGFuZCBvbmUuIFNvIHRoZSBmYXJ0aGVyIGF3YXkgeW91IGFyZSBmcm9tIHplcm8sIG1lYW5zIHRoZSBjbG9zZXIgeW91IGFyZSB0byBvbmUuIElmIHdlIGFkZCB0aGVzZSB0d28gdGVybXMgdG9nZXRoZXIsIHdlIHNlZSB0aGF0IGl0IGZvcm1zIGEgY29uY2F2ZSBjdXJ2ZS0tDQoNCm9yIEkgc2hvdWxkIHNheSwgYSBjb252ZXggY3VydmUtLQ0KDQp3aXRoIGEgd2VsbC1kZWZpbmVkIG1pbmltdW0uIEFuZCB0aGlzIGlzIHdoYXQgd2UgYWx3YXlzIHdhbnRlZCBmb3IgYW55IG9mIG91ciBsb3NzIG1pbmltaXphdGlvbnMuIFdlIHdhbnQgYSB3ZWxsIGRlZmluZWQgbWluaW11bS4gV2VsbCwgd2Ugc2VlIHRoYXQgdGhpcyBsb2dhcml0aG1pYyBsb3NzIGhhcyB0aGF0IHdlbGwgZGVmaW5lZCBtaW5pbXVtLCBhbmQgaXQncyBhIGZ1bmN0aW9uIG9mIHRoZXNlIHR3byB0ZXJtcy4NCg0KU28gYnkgdXNpbmcgdGhlc2UgdHdvIHRlcm1zIHdpdGggdGhlIHZhbHVlcyBvZiB5IGVxdWFscyBvbmUgYW5kIHkgZXF1YWxzIHplcm8sIHdlIGZvcm1lZCB0aGlzIGxvc3MgZnVuY3Rpb24gd2l0aCBhIHdlbGwgZGVmaW5lZCBtaW5pbXVtLiBUaGF0IGdpdmVzIHVzIGFuIGFkdmFudGFnZSBiZWNhdXNlIGEgd2VsbCBkZWZpbmVkIGEgbWluaW11bSBmb3IgdXMgaXMgZWFzeSB0byBmaW5kIGFuZCBtaW5pbWl6ZS4NCg0KDQoNCjUuIG1pbmltaXppbmcgbG9nIGxvc3MNCg0KSGksIGFuZCB3ZWxjb21lIHRvIHRoZSB2aWRlbyBvbiBtaW5pbWl6aW5nIGxvZyBsb3NzLiBTbywgb25jZSB3ZSBoYXZlIGEgbG9nIGxvc3MgZXF1YXRpb24sIG91ciBxdWVzdGlvbiBiZWNvbWVzLCBob3cgZG8gd2UgbWluaW1pemUgdGhhdD8gQW5kIG9mIGNvdXJzZSwgbWluaW1pemluZyBvdXIgbG9nIGxvc3MsIG1lYW5zIHRoYXQgb3VyIG1vZGVsIGlzIHByb2R1Y2luZyBnb29kIHByZWRpY3Rpb25zLiBTbyBhIHNtYWxsIGxvc3MsIG9yIGEgbWluaW1hbCBsb3NzLCBnaXZlcyB1cyB3aGF0IHdlIHdhbnQtLQ0KDQphIGdvb2QsIHdlbGwtZml0IG1vZGVsLiBXZSd2ZSBkaXNjdXNzZWQgdGhlIGxvc3MgZnVuY3Rpb24uIFdlIGhhdmUgYSBwcmVkaWN0aW9uIGZ1bmN0aW9uLiBTbyBpZiB3ZSB0YWtlIHRoZSBzaWdtb2lkIHByZWRpY3Rpb24gZnVuY3Rpb24gYW5kIG91ciBsb2dhcml0aG1pYyBsb3NzLCBsZXQncyB0cnkgYW5kIGNvbWJpbmUgdGhlbSBpbiBhIHNpbWlsYXIgZmFzaGlvbiB0byBsaW5lYXIgcmVncmVzc2lvbi4NCg0KQW5kIGtlZXAgaW4gbWluZCB0aGF0IGFsdGhvdWdoIHdlIHNlZSB0aGUgdmFsdWUgcCwgcCBpcyByZWFsbHkgYSBmdW5jdGlvbiBvZiB4LiBBbmQgYWx0aG91Z2ggSSBzaG91bGRuJ3Qgc2F5IGZ1bmN0aW9uIG9mIHgsIHAgY29udGFpbnMgdGhlIGRhdGEsIHgsIGFuZCBpcyBhY3R1YWxseSBhIGZ1bmN0aW9uIG9mIHRoZSBzbG9wZXMgdGltZXMgeC4gU28gd2UndmUgZ290IGEgbGl0dGxlIGJpdCBvZiBtYXRoIGhlcmUuIEJ1dCBpdCdzIGltcG9ydGFudCBmb3IgdXMgdG8gdW5kZXJzdGFuZCBob3cgdGhpcyB3b3JrcywgYW5kIGhvdyBpdCdzIHNpbWlsYXIgdG8gbGluZWFyIHJlZ3Jlc3Npb24uDQoNClNvIHRyYWRpdGlvbmFsbHksIHdlIGRlbm90ZSB0aGUgbG9zcyB3aXRoIHRoZSB2YWx1ZSBKLiBBbmQgaGVyZSwgd2hhdCBJJ20gZG9pbmcgaXMgSSdtIHN1bW1pbmcgdXAgYWxsIHRoZSBsb3NzIHRlcm1zIGZvciBlYWNoIHByZWRpY3Rpb24uIFNvIGFnYWluLCBpZiB0aGUgcHJlZGljdGlvbiBpcyAxLCB3ZSdyZSBnb2luZyB0byB1c2UgdGhlIGxlZnQgaGFuZCB0dXJuLiBJZiB0aGUgcHJlZGljdGlvbiBpcyAwLCB3ZSdyZSBnb2luZyB0byB1c2UgdGhlIHJpZ2h0IGhhbmQgdGVybS4NCg0KT3IgSSBzaG91bGRuJ3Qgc2F5IHRoZSBwcmVkaWN0aW9uLCBpZiB0aGUgdGFyZ2V0LiBBbmQgYXMgd2Ugc3VtIGFsbCBvZiB0aG9zZSB1cCwgd2Ugd2lsbCBnZXQgdGhlIGZpbmFsIGxvc3MuIEFuZCBqdXN0IGEgcXVpY2sgcmVtaW5kZXIgdGhhdCBJIGhhdmUgcCBpbiB0aGUgbG9zcyBlcXVhdGlvbi4gQnV0IHJlbWVtYmVyIHRoYXQgcCBpcyBhY3R1YWxseSB0aGUgc2lnbW9pZCB0aGF0IGNvbnRhaW5zIG91ciBkYXRhIGFuZCBvdXIgc2xvcGVzLg0KDQpBbmQgcmVtZW1iZXIsIHRoYXQncyB0aGUgc2FtZSBhcyBsaW5lYXIgcmVncmVzc2lvbiwgdGhhdCBzdW0gb2YgbWkgeGkuIFNvLCB3ZSd2ZSBnb3QgdGhpbmdzIHRoYXQgYXJlIGZpeGVkLiBPdXIgdGFyZ2V0LCB5LCBpcyBmaXhlZC4gVGhhdCdzIG91ciB0YXJnZXQuDQoNCkl0J3Mgb3VyIGRhdGEuIE91ciB4IHZhbHVlcyBhcmUgZml4ZWQuIFRoYXQncyBvdXIgZGF0YS4gU28gdG8gbWFrZSBhIG1pbmltdW0gdmFsdWUgb2YgSiwgdGhlIG9ubHkgdGhpbmcgdGhhdCBjYW4gY2hhbmdlIGlzIHRoZSBzbG9wZS4NCg0KQW5kIGJlY2F1c2Ugd2UgaGF2ZSB0aGlzIGZ1bmN0aW9uIHRoYXQgaGFzIGEgd2VsbC1kZWZpbmVkIG1pbmltdW0sIGlmIHdlIHRha2UgdGhlIHNsb3BlIG9mIHRoZSBmdW5jdGlvbiBKLCB3aGF0IHdlIGNhbiBkbyBpcyB3ZSBjYW4gZmluZCB0aGUgbWluaW11bS4gQmVjYXVzZSB3aGF0IHdlJ2xsIGRvIGJ5IGxvb2tpbmcgYXQgdGhlIHNsb3BlIGlzIHdlJ2xsIGZpbmQsIHdoaWNoIHdheSBkb2VzIHRoZSBkaXJlY3Rpb24gb2YgdGhlIG1pbmltdW0gbGllPyBEb2VzIGl0IGxpZSB0byBvdXIgbGVmdD8gRG9lcyBpdCBsaWUgdG8gb3VyIHJpZ2h0IGluIGVhY2ggZGltZW5zaW9uPw0KDQpBbmQgc28gYnkgdGFraW5nIHRoZSBwYXJ0aWFsIGRlcml2YXRpdmUgb2YgSiB3aXRoIHJlc3BlY3QgdG8gdGhlIHNsb3BlLCB3ZSB3aWxsIGdldCBhbiB1cGRhdGUgcnVsZSBmb3IgdGhlIHZhbHVlIG9mIHRoZSBzbG9wZS4gTm93IGNvbnZlbmllbnRseSwgdGhlIHVwZGF0ZSBydWxlIGZvciBsb2dpc3RpYyByZWdyZXNzaW9uIGlzIHRoZSBleGFjdCwgZG93biB0byB0aGUgdmFyaWFibGUgZnVuY3Rpb24gYXMgdGhlIGxpbmVhciByZWdyZXNzaW9uLiBJbiBvdGhlciB3b3JkcywgSSBoYXZlIHRoaXMgc2lnbW9pZCBmdW5jdGlvbi4gQW5kIEkgaGF2ZSBhbiB1cGRhdGUgcnVsZS4NCg0KQW5kIHdoZW4gSSB0YWtlIHRoZSBhY3R1YWwgZGVyaXZhdGl2ZSBvZiB0aGUgdXBkYXRlIHJ1bGUsIGl0IHR1cm5zIG91dCB0aGF0IHRoZSB2YWx1ZSBpcyB0aGUgc2FtZS4gTm93LCBhbHRob3VnaCBpdCdzIGEgbmljZXR5IHRvIHNlZSwgSSd2ZSBpbmNsdWRlZCBhIGxpbmsgaGVyZSBzbyB0aGF0IHlvdSBjYW4gZ28gdGhyb3VnaCB0aGUgZGV0YWlscyBvZiB0aGUgZGVyaXZhdGlvbiB5b3Vyc2VsZi4gQnV0IHRoZSBrZXkgdGFrZWF3YXkgZnJvbSBhbGwgdGhpcyBtYXRoZW1hdGljcyBpcyBvdXIgdXBkYXRlIGlzIHRoZSBzYW1lLiBBbmQgaWYgb3VyIHVwZGF0ZSBpcyB0aGUgc2FtZSwgdGhhdCBtZWFucyB0aGF0IHdlIGNhbiB1c2UgYWxsIHRoZSBhbGdvcml0aG1zIGZyb20gbGluZWFyIHJlZ3Jlc3Npb24gdG8gdXBkYXRlIG91ciB2YWx1ZXMgb2YgdGhlIHNsb3BlIGZvciBsb2dpc3RpYyByZWdyZXNzaW9uLg0KDQpTbyBub3cgd2UndmUgZXh0ZW5kZWQgb3VyIGFiaWxpdHkgdG8gbW9kZWwgcHJvYmxlbXMgZnJvbSBsaW5lYXIgcHJvYmxlbXMgdG8gY2F0ZWdvcmljYWwgcHJvYmxlbXMsIHNpbXBseSBieSB1c2luZyBhbiBhcHByb3ByaWF0ZSBsb3NzIGZ1bmN0aW9uLiBTbyBub3csIGhvcGVmdWxseSBpdCBzdGFydHMgdG8gYmVjb21lIGEgbGl0dGxlIGJpdCBjbGVhcmVyIHdoeSB3ZSB1c2UgdGhpcyBwYXJ0aWN1bGFyIGV4cG9uZW50aWFsIGZ1bmN0aW9uLiBJdCB3YXMgYWN0dWFsbHkgY2hvc2VuIHNvIHRoYXQgdGhvc2UgdXBkYXRlIHJ1bGVzIGNvbWUgb3V0IHRoZSBzYW1lLiBBbmQgYWdhaW4sIGp1c3QgYSBxdWljayByZW1pbmRlciwgYWx0aG91Z2ggSSd2ZSBnb3QgbG9nLCBMLU8tRywgdGhlc2UgYXJlIGFjdHVhbGx5IG5hdHVyYWwgbG9ncy4NCg0KQW5kIHRoYXQgY2FuIGJlIGltcG9ydGFudCBpZiB5b3UncmUgdGFraW5nIHRob3NlIGRlcml2YXRpdmVzLCBiZWNhdXNlIHRoZSBuYXR1cmFsIGxvZyBkZXJpdmF0aXZlIGlzIGFjdHVhbGx5IDEgb3ZlciB0aGUgdmFsdWUuIFNvIHRoZSBkZXJpdmF0aXZlIG9mIG5hdHVyYWwgbG9nIG9mIHAgaXMgMSBvdmVyIHAuIFRoYXQgcGxheXMgYW4gaW1wb3J0YW50IHJvbGUgaW4gdGhpcyB1cGRhdGUgcnVsZSBjb21pbmcgb3V0IHRoZSBzYW1lLg0KDQoNCg0KNi4gbXVsdGljbGFzcw0KDQpIaSwgYW5kIHdlbGNvbWUgdG8gdGhlIHZpZGVvIG9uIG11bHRpY2xhc3MgY2xhc3NpZmljYXRpb24uIFNvIHRoZSBxdWVzdGlvbiBtYXkgY29tZSB1cCB0byB5b3VyIG1pbmQuIFdlJ3ZlIGJlZW4gc3R1ZHlpbmcgaG93IHRvIGRvIGJpbmFyeSBjbGFzc2lmaWNhdGlvbnMuIEFuZCBvdXIgc2lnbW9pZCBmdW5jdGlvbiBnaXZlcyB1cyBhIGJpbmFyeSBvdXRwdXQtLQ0KDQpjbGFzcyAxIG9yIGNsYXNzIDAuIFNvIHlvdSBtYXkgY29tZSBhY3Jvc3MgdGhlIGxvZyBsb3NzIGFzIGJpbmFyeSBjcm9zcyBlbnRyb3B5IGxvc3MuIEJ1dCBhcyB3ZSBleHRlbmQgdG8gbW9yZSBhbmQgbW9yZSBjb21wbGV4IHByb2JsZW1zLCBub3QgZXZlcnkgcHJvYmxlbSBpcyBhIHNpbXBsZSBleGFtcGxlIG9mIHR3byBjbGFzc2VzLiBXZSBjb3VsZCBoYXZlIHRocmVlIGNsYXNzZXMsIDEwIGNsYXNzZXMsIDEsMDAwIGNsYXNzZXMsIG9yIGV2ZW4gYSBtaWxsaW9uIGNsYXNzZXMuDQoNClNvIGhvdyBjYW4gd2UgZ2V0IGZyb20gb3VyIGJpbmFyeSBjbGFzc2lmaWNhdGlvbiwgdGhlIG91dHB1dCBvZiB0aGUgc2lnbW9pZCBiZWluZyAwIG9yIDEsIHRvIG11bHRpcGxlIGNsYXNzZXM/IEFuZCBhIHNpbXBsZSBleGFtcGxlIG9mIHRoaXMgaXMgdGhlIGlyaXMgZGF0YSBzZXQgdGhhdCdzIGEgY2xhc3NpYyB0b3kgZGF0YSBzZXQuIFRoZXJlIGFyZSB0aHJlZSBzcGVjaWVzIG9mIGZsb3dlciB3aXRoaW4gdGhlIGlyaXMuIEFuZCB0aGUgcXVlc3Rpb24gd291bGQgYmVjb21lLCBob3cgd291bGQgSSBkbyBhIGxvZ2lzdGljIHJlZ3Jlc3Npb24gb24gdGhhdD8NCg0KQmVjYXVzZSBhIHNpZ21vaWQgb3V0cHV0IHdpbGwgb25seSBiZSAwIHRvIDEsIGJ1dCBJJ3ZlIHNvbWVob3cgZ290IHRocmVlIGNsYXNzZXMuIEFuZCBldmVuIGlmIEkgZGlkIGEgb25lLWhvdCBpbmNsdWRpbmcsIG15IHRhcmdldCBpcyBubyBsb25nZXIgMSBvciAwLCB0aGVyZSdzIGFjdHVhbGx5IHRocmVlIGNsYXNzZXMgdGhhdCBoYXZlIG9uZXMgYW5kIHplcm9zLiBTbyBob3cgd291bGQgSSBhZGFwdCBteSBsb2dpc3RpYyByZWdyZXNzaW9uIHRvIGRlYWwgd2l0aCB0aGVzZSBtdWx0aXBsZSBjbGFzc2VzPyBTbyB0aGUgZmlyc3QgbWV0aG9kIHRoYXQgd2UgY2FuIHVzZSBpcyBzb21ldGhpbmcgdGhhdCdzIGNhbGxlZCBvbmUtdmVyc3VzLWFsbCBvciBvbmUtdmVyc3VzLXRoZS1yZXN0Lg0KDQpTbyB3aGF0IHdlJ3JlIGdvaW5nIHRvIGRvIGlzIGZvciBlYWNoIGNsYXNzLCB3ZSB3aWxsIHRyYWluIGEgZGlzY3JpbWluYXRvciBvciBhIG1vZGVsLiBTbyBpZiBJIGhhdmUgdGhyZWUgY2xhc3NlcyB0aGF0IGNvcnJlc3BvbmQgdG8gdGhlIGNvbG9ycyByZWQsIGJsdWUsIGFuZCBncmVlbiwgbXkgY2xhc3NpZmljYXRpb25zIHdvdWxkIGJlIHJlZC9ub3QgcmVkLCBncmVlbi9ub3QgZ3JlZW4sIGJsdWUvbm90IGJsdWUuIFNvIEkndmUgZ290IGEgY2xhc3NpZmllciBmb3IgZWFjaCBjbGFzcy4gQW5kIHdoYXQgd2UncmUgZ29pbmcgdG8gZG8gaXMgd2UnbGwgZmluZCB3aGljaCBjbGFzcyBnZXRzIHRoZSBoaWdoZXN0IG91dHB1dCB2YWx1ZS4NCg0KV2hhdCBoYXBwZW5zIGlzIHlvdSdsbCBsb29rIGF0IHRoZSBzaWdtb2lkLCBhbmQgYWx0aG91Z2ggd2Ugb2Z0ZW4gdGFsayBhYm91dCB0aGUgc2lnbW9pZCBvdXRwdXR0aW5nIGEgMCBhbmQgMSwgaW4gcmVhbGl0eSwgd2UgZ2V0IHNvbWUgdmFsdWUgYmV0d2VlbiAwIGFuZCAxLiBBbmQgc28gdG8gcGljayB0aGUgd2lubmluZyBjbGFzcywgd2hhdCB3ZSB3aWxsIGRvIGlzIGZpbmQgb3V0IHdoaWNoIG91dHB1dCBpcyB0aGUgbGFyZ2VzdC4gTm93IHdlIGZyYW1lIHRoZXNlIHByb2JsZW1zIGFzIHdoZW4gd2UgZG8gdGhlIHJlZC9ub3QgcmVkLCByZWQgd291bGQgYmUgY2xhc3MgMSwgYW5kIG5vdCByZWQgd291bGQgYmUgY2xhc3MgMC4gVGhhdCB3YXkgdGhlIG5vdCB2YWx1ZSBpcyBhbHdheXMgY2xhc3MgMCwgYW5kIHRoZSB2YWx1ZSBpcyBjbGFzcyAxLg0KDQpTbyB3ZSB3aWxsIGNvbWUgb3V0IHdpdGggdGhyZWUgdmFsdWVzLiBBbmQgd2hhdCB3ZSB3aWxsIGRvIGlzIHdlIHdpbGwgbG9vayBhdCB0aG9zZSB0aHJlZSB2YWx1ZXMsIGFuZCB0aGV5IG1heSBhbGwgYmUgdmVyeSBsYXJnZSBhbmQgdmVyeSBwb3NpdGl2ZS4gVGhlIGtleSBpcywgd2hpY2ggb25lIGlzIHRoZSBsYXJnZXN0PyBTbyBpZiB3ZSBsb29rIGFuZCBzYXkgcmVkIGhhcyBhIHNjb3JlIG9mIDAuOTcNCg0KYW5kIGdyZWVuIGhhcyBhIHNjb3JlIG9mIDAuMjIgYW5kIGJsdWUgaGFzIGEgc2NvcmUgb2YgMC41MCwgd2VsbCwgYmVjYXVzZSByZWQgaGFzIHRoZSBoaWdoZXN0IHNjb3JlLCB0aGF0J3MgdGhlIHdpbm5pbmcgbW9kZWwuIFNvIHdlJ3JlIHRyeWluZyB0byBhcHBseSBvdXIgbW9kZWxzIG9mZiBhZ2FpbnN0IGVhY2ggb3RoZXIuDQoNCkl0J3Mgbm90IHJlYWxseSBhIGNvbXBldGl0aW9uLCBidXQgd2UncmUgbG9va2luZyBhdCBlYWNoIG9mIHRoZSBvdXRwdXQgc2NvcmVzIGFuZCBmaW5kaW5nIHdoaWNoIG9uZSBnaXZlcyB1cyB0aGUgbWF4IHNjb3JlLiBBbmQgb2Z0ZW50aW1lcywgd2UnbGwgc2VlIGEgZnVuY3Rpb24gY2FsbGVkIGFyZ21heCwgd2hpY2ggbWVhbnMgaXQgd2lsbCBsb29rIGF0IHRoZSBvdXRwdXRzIG9mIGFsbCB0aHJlZSB2YWx1ZXMgYW5kIGp1c3QgdGFrZXMgdGhlIG1heGltdW0gdmFsdWUgYW5kIGxvb2sgZm9yIHdoaWNoIG9uZSBpcyBtYXhpbXVtLCBhbmQgaXQgcmV0dXJucyB0aGF0IGNsYXNzLiBUaGF0J3MgdGhlIG9uZS12ZXJzdXMtcmVzdCBvciBvbmUtdmVyc3VzLWFsbCBtZXRob2QuIFRoZSBkb3duc2lkZSwgb2YgY291cnNlLCBpcyB5b3UgaGF2ZSB0byBrZWVwIGEgbW9kZWwgZm9yIGVhY2ggb2YgeW91ciBjbGFzc2VzLg0KDQpTbyB0aGlzIGlzbid0IHNvIGJhZCBpZiB3ZSd2ZSBnb3QgdGhyZWUgY2xhc3Nlcy4gQnV0IGlmIHdlIGhhZCBhIGxvdCBvZiBjbGFzc2VzLS0NCg0KMTAwLCBvciAxLDAwMCwgb3IgZXZlbiB0aGF0IDEwMCwwMDAsIG1pbGxpb24gY2xhc3Nlcy0tDQoNCnRoYXQgY291bGQgYmUgcXVpdGUgdGltZSBjb25zdW1pbmcuIFNvIHRoYXQgaXMgb25lIG9mIHRoZSBkcmF3YmFja3Mgb2YgbG9naXN0aWMgcmVncmVzc2lvbi4gSXQncyBub3QgaW5oZXJlbnRseSBtdWx0aWNsYXNzLCBidXQgd2UgY2FuIGFkYXB0IHRvIGl0LiBCdXQgYXMgdGhlIG51bWJlciBvZiBjbGFzc2VzIGluY3JlYXNlcywgaXQgYmVjb21lcyBtb3JlIGFuZCBtb3JlIGRpZmZpY3VsdCBmb3IgdXMgdG8gZmluZCBhIHNvbHV0aW9uIHRoYXQncyBlYXN5IGZvciB1cyB0byBpbXBsZW1lbnQuDQoNCkl0J3MgaGFyZCB0byBtYW5hZ2UgbWlsbGlvbnMgb2YgbW9kZWxzLiBJdCdzIG5vdCBzbyBiYWQgdG8gbWFuYWdlIHRocmVlIG1vZGVscy4gU28gaGVyZSBpcyBhbiBleGFtcGxlIG9mIHNvbWUgZGVjZW50IG91dHB1dHMgb2YgdGhlIHJlZCwgYmx1ZSwgYW5kIGdyZWVuLiBTbyB3ZSBsb29rIGF0IHRoZSByZWQgc2NvcmUsIDAuODcsDQoNCnRoZSBibHVlIHNjb3JlLCAwLjIyLCB0aGUgZ3JlZW4gc2NvcmUsIDAuNDUuIFNvIHdlIHdvdWxkIGxvb2sgYXQgdGhvc2UgYW5kIGZvcm0gYSB2ZWN0b3Igb2YgdGhlIHBvc2l0aXZlIG91dHB1dHMuIEFuZCB3aGVuIEkgc2F5IHRoZSBwb3NpdGl2ZSBvdXRwdXRzLCB0aGUgY2xhc3MgMSBvdXRwdXRzLg0KDQpTbyBoZXJlIEkgaGF2ZSAwLjg3LCAwLjIyLCBhbmQgMC40NS4gU28gd2l0aCAwLjg3IGJlaW5nIHRoZSBsYXJnZXN0IHZhbHVlIGNvcnJlc3BvbmRpbmcgdG8gdGhlIHJlZCB2YWx1ZSwgb25jZSBhZ2Fpbiwgd2UncmUgZ29pbmcgdG8gY2xhc3NpZnkgdGhhdCBhcyByZWQuDQoNClNvIG9uZSBvZiB0aGUgaXNzdWVzIGlzIGlmIHlvdXIgZGF0YSBpcyBub3QgZXZlbmx5IGRpc3RyaWJ1dGVkLCB3aGF0IG91ciBjbGFzc2lmaWVycyBhcmUgZ29pbmcgdG8gc2VlIGlzIGEgbGFyZ2UgbnVtYmVyIG9mIG5lZ2F0aXZlIGV4YW1wbGVzLiBBbmQgYmVjYXVzZSBjbGFzc2lmaWVycyBsZWFybiBiYXNlZCBvbiBwcm9iYWJpbGl0eSwgdGhleSBtYXkgc3RhcnQgdG8gZmF2b3IgdGhlIG5vdCBleGFtcGxlLiBTbyBsZXQncyBzYXkgb3VyIGRhdGEgaXMgZXZlbmx5IGRpc3RyaWJ1dGVkLCB3aXRoIDEvMyByZWQsIDEvMyBibHVlLCBhbmQgMS8zIGdyZWVuLiBXZWxsIHdoZW4gSSBnbyB0byB0aGUgcmVkL25vdCByZWQgY2xhc3NpZmllciwgMS8zIG9mIHRoZSBkYXRhIGlzIHJlZCwgYnV0IDIvMyBvZiB0aGUgZGF0YSBpcyBub3QgcmVkLg0KDQpBbmQgdGhpcyBjYW4gaGF2ZSB0aGUgZWZmZWN0IG9mIGJpYXNpbmcgb3VyIG1vZGVscyB0b3dhcmRzIHRoZSAwIGNsYXNzIHByb2JhYmlsaXR5LiBTbyBrZWVwIHRoYXQgaW4gbWluZCwgZXNwZWNpYWxseSBhcyB0aGUgbnVtYmVyIG9mIGNsYXNzZXMgaW5jcmVhc2VzLiBUaGlzIHdvdWxkIGdldCB3b3JzZSBpZiBJIGhhZCAxMCBjbGFzc2VzLCAxMCBjb2xvcnMuIFRoZW4gMTAlIG9mIHRoZSBkYXRhIHdvdWxkIGJlIHJlZCBhbmQgOTAlIG9mIHRoZSBkYXRhIHdvdWxkIGJlIG5vdCByZWQuDQoNClNvIGFzIGEgYmFzaWMgZ3Vlc3MgdG8gZ2V0IDkwJSBhY2N1cmFjeSwgeW91IGNvdWxkIGFjdHVhbGx5IHByZWRpY3QgMTAwJSBvZiB0aGUgZGF0YSBiZWluZyBub3QgcmVkLiBUaGF0J3MgYW4gZXhhbXBsZSBvZiBiaWFzaW5nIHRoZSBkYXRhLiBOb3cgaW4gcmVhbGl0eSwgdGhlIGNsYXNzaWZpZXIgd2lsbCBob3BlZnVsbHkgcGljayB1cCB3aGljaCBpbnB1dCB2YXJpYWJsZXMgY29udHJpYnV0ZSB0byB0aGUgY2xhc3MgcmVkLiBCdXQgYmUgYXdhcmUgdGhhdCBhcyB5b3UgZ2V0IG1vcmUgYW5kIG1vcmUgY2xhc3NlcywgeW91ciBjbGFzc2lmaWVycyB3aWxsIHN0YXJ0IHRvIGdldCBiaWFzZWQgZm9yIG5lZ2F0aXZlIHNhbXBsZXMuDQoNCkFuZCB0aGVyZSBhcmUgYSBudW1iZXIgb2YgdGhpbmdzIHRoYXQgd2UgY2FuIGRvIHRvIHRha2UgY2FyZSBvZiB0aGF0IGJpYXMsIGJ1dCB5b3UgaGF2ZSB0byBiZSBhd2FyZSBhbmQgZnVuZGFtZW50YWxseSBhZGRyZXNzIHRoZW0gYXMgeW91IGdldCB0byBsYXJnZXIgYW5kIGxhcmdlciBtdWx0aWNsYXNzLiBOb3csIHRoZXJlJ3MgYW5vdGhlciB3YXkgdG8gaGFuZGxlIHRoaXMgbXVsdGljbGFzcywgYW5kIGl0J3MgbW9yZSBvZiBhIGhlYWQtdG8taGVhZCBiYXR0bGUgb2YgY2xhc3NpZmllcnMuIEFuZCBhZ2Fpbiwgd2UgdXNlIHRoZXNlIHdoaWNoIG9uZSdzIHRoZSBtYXgsIHdoaWNoIG9uZSdzIHRoZSBiZXN0LS0NCg0KYSBoZWFkLXRvLWhlYWQgYmF0dGxlLiBCdXQgaXQncyBzZXR0aW5nIHVwIGEgY29tcGFyaXNvbi4gU28gd2UncmUgZ29pbmcgdG8gZG8gYSBjb21wYXJpc29uIGZvciBhbGwgdGhlc2UgY2xhc3NpZmllcnMuIFRoZSBwcm9ibGVtIGlzIHRoaXMgY2FuIGdldCBvdXQgb2YgaGFuZCBxdWl0ZSBxdWlja2x5IGFzIHdlIGNhbiBzZWUgdGhlIG51bWJlciBvZiBjbGFzc2lmaWVycyBzdGFydHMgdG8gZXhwYW5kIHF1aXRlIHF1aWNrbHkuDQoNClNvIGhlcmUsIHdlJ3JlIGdvaW5nIHRvIGJlIGV2ZW4gd29yc2UgYXMgd2UgZ28gdG8gbXVsdGlwbGUgY2xhc3Nlcy4gQW5kIHdoYXQgd2UncmUgZ29pbmcgdG8gZG8gaXMgY29tcGFyZSB0aGluZ3MgbGlrZSwgaXMgaXQgcmVkPyBJcyBpdCBncmVlbj8gSXMgaXQgZ3JlZW4/DQoNCklzIGl0IGJsdWU/IEFzIHdlIGdvIHRocm91Z2ggdGhhdCBzZXF1ZW5jZSwgb2J2aW91c2x5IHdlIGNhbiBzdGFydCB0byBzZWUgdGhlcmUncyBmYWN0b3JpYWxzIGludm9sdmVkLiBCdXQgdGhhdCdzIHdoeSB3ZSBzZWUgdGhlIG51bWJlciBvZiBjbGFzc2lmaWVycyBleHBhbmRpbmcgcXVpdGUgYSBiaXQgbW9yZSB0aGFuIHdlIGhhZCBmb3IgdGhlIG9uZS12ZXJzdXMtYWxsLiBXaGVyZWFzIGlmIHdlIGhhZCAxMCBjbGFzc2VzLCB3ZSB3b3VsZCBoYXZlIDEwIGNsYXNzaWZpZXJzLg0KDQpCdXQgdGhpcyBhbHNvIGFsbG93cyBkaXJlY3QgY29tcGFyaXNvbnMgYmV0d2VlbiB0aGUgZGlmZmVyZW50IGNsYXNzZXMuIEFuZCB0aGUgaWRlYSBoZXJlIGlzIHRoZSBjbGFzcyB0aGF0IHdpbnMgdGhlIG1vc3Qgdm90ZXMgaXMgdGhlIGFzc2lnbmVkIGNsYXNzLiBTbyBmb3IgdGhlIGV4YW1wbGUgb2YgcmVkIGFuZCBncmVlbiBhbmQgYmx1ZSwgd2Ugd291bGQgaGF2ZSBhIHJlZC9ncmVlbiBhbmQgYSByZWQvYmx1ZS4gU28gcmVkIGhhcyB0d28gb3Bwb3J0dW5pdGllcyB0byB3aW4uDQoNCldlbGwsIGp1c3QgbGlrZSB0aGF0LCBpZiB5b3UndmUgZ290IHRoZSByZWQvZ3JlZW4sIHlvdSd2ZSBhbHNvIGdvdCBncmVlbiBhbmQgYmx1ZS4gU28gZ3JlZW4gaGFzIHR3byBjaGFuY2VzIHRvIHdpbiwgYW5kIHNvIGRvZXMgYmx1ZS4gVGhhdCdzIHdoeSB5b3Ugc2VlIGFzIHRoZSBudW1iZXIgb2YgY2xhc3NlcyBleHBhbmRzLCB0aGUgbW9yZSBoZWFkLXRvLWhlYWRzIHdlIHRlbmQgdG8gZ2V0LiBBbmQgdGhpcyBjYW4gbGVhZCB0byBzb21lIGNyYXp5IHNvbHV0aW9ucyB3aGVyZSBzb21lIG9mIHRoZSBjbGFzc2VzIGFyZW4ndCBzdXBlciBwb3B1bGFyLCBidXQgaXQncyB2ZXJ5IGRpZmZpY3VsdCBmb3Igb3VyIGNsYXNzaWZpZXJzIHRvIGRpc3Rpbmd1aXNoIGJldHdlZW4gZGlmZmVyZW50IHBhcnRzLg0KDQpTbyBvbmUtdmVyc3VzLW9uZSwgYWx0aG91Z2ggaXQgaXMgdXNlZCBpbiBzbWFsbGVyIGNsYXNzaWZpZXJzLCBpdCdzIHR5cGljYWxseSBub3QgdXNlZCBhcyB3ZSBnbyB0byBsYXJnZXIgYW5kIGxhcmdlciBudW1iZXJzIG9mIGNsYXNzZXMuIEJ1dCB0aGUgb25lLXZlcnN1cy1vbmUgaXMgb3V0IHRoZXJlLCBhbmQgaW4gbWFueSBvZiBvdXIgYWxnb3JpdGhtcyBpcyBpbXBsZW1lbnRlZCBiZWhpbmQgdGhlIHNjZW5lcywgc28geW91IGRvbid0IGhhdmUgdG8gd3JpdGUgdGhpcyBmcm9tIHNjcmF0Y2guIFlvdSBjYW4gc2F5LCBJIHdhbnQgdG8gZG8gYSBvbmUtdG8tb25lIGNsYXNzaWZpZXIuIEhlcmUgYXJlIG15IGlucHV0cywgaGVyZSBhcmUgbXkgdGFyZ2V0cywgYW5kIHRoZSBhbGdvcml0aG0gd2lsbCBzZXQgdXAgYWxsIHRoZXNlIGNsYXNzaWZpZXJzIGZvciB5b3UuDQoNClNvIGhlcmUgSSd2ZSBnb3QgYSBmb3VyLXZhbHVlIGV4YW1wbGUgb2YgVGV4YXMsIElvd2EsIENhbGlmb3JuaWEsIGFuZCBGbG9yaWRhLiBOb3cgaXQgdHVybnMgb3V0IEkgbmVlZCBzaXggaGVhZC10by1oZWFkIGNsYXNzaWZpZXJzLiBTbyBiZWNhdXNlIFNNVSBpcyBpbiBUZXhhcywgSSBwdXJwb3NlbHkgc2V0IHRoaXMgdXAgc28gdGhhdCBUZXhhcyB3b3VsZCB3aW4uIEFuZCB3ZSBkb24ndCBhY3R1YWxseSBrbm93IHdoYXQgdGhlc2UgdmFsdWVzIGFyZSwgYnV0IHRoaXMgaXMganVzdCBhbiBleGFtcGxlLg0KDQpBbmQgc28gSSd2ZSBnb3QgdGhlIFRleGFzLUZsb3JpZGEgY2xhc3NpZmllci4gQW5kIHlvdSBjYW4gc2VlIHRoYXQgVGV4YXMgc2NvcmUgaXMgdGhlIGxhcmdlc3QgdmFsdWUgdGhlcmUuIFNvIFRleGFzIGlzLCBxdW90ZSB1bnF1b3RlLCAidGhlIHdpbm5lciIgb2YgdGhhdCBjbGFzc2lmaWVyLiBBbmQgd2UgaGF2ZSB0aGUgVGV4YXMgdmVyc3VzIElvd2EuDQoNCkFuZCB5b3UgY2FuIHNlZSBvbmNlIGFnYWluLCBUZXhhcyBjb21lcyBvdXQgb24gdG9wLiBXZWxsIHRoZW4gd2UndmUgZ290IHRoZSBUZXhhcyB2ZXJzdXMgQ2FsaWZvcm5pYS4gQW5kIHdlIHNlZSBvbmNlIGFnYWluLCBUZXhhcyBpcyB0cml1bXBoYW50LiBBbGwgb2Ygb3VyIHN0dWRlbnRzIGluIFRleGFzIGFyZSBjZWxlYnJhdGluZyByaWdodCBhdCB0aGlzIHBvaW50Lg0KDQpOb3cgd2UgZ28gdG8gdGhlIENhbGlmb3JuaWEgdmVyc3VzIElvd2EuIEJlZm9yZSB3ZSB3ZXJlIGxvb2tpbmcgYXQgVGV4YXMsIGFsbCB0aGUgVGV4YXMgaGVhZC10by1oZWFkcy4gTm93IGxldCdzIGxvb2sgYXQgdGhlIENhbGlmb3JuaWEgaGVhZC10by1oZWFkcy4gU28gQ2FsaWZvcm5pYSBoYXMgYWxyZWFkeSBnb25lIGhlYWQtdG8taGVhZCB3aXRoIFRleGFzLg0KDQpOb3cgaXQgbmVlZHMgdG8gZ28gaGVhZC10by1oZWFkIHdpdGggSW93YSBhbmQgRmxvcmlkYS4gQW5kIHlvdSBjYW4gc2VlIGl0IGNvbWVzIG91dCBvbiB0aGUgbG9zaW5nIGVuZCB2ZXJzdXMgSW93YS4gU28gSW93YSB3aW5zIHRoYXQgaGVhZC10by1oZWFkIGJhdHRsZS4gQW5kIENhbGlmb3JuaWEgY29tZXMgb3V0IG9uIHRoZSBib3R0b20gYWdhaW5zdCBGbG9yaWRhLg0KDQpTbyBGbG9yaWRhIGhhcyB3b24gb25lLiBTbyBvdXIgc2NvcmUgbm93IGlzIFRleGFzIDMsIElvd2EgMSwgRmxvcmlkYSAxLiBTbyB3ZSd2ZSBnb3QgbGVmdCBpcyB0aGUgRmxvcmlkYS1Jb3dhIGJhdHRsZS4gQW5kIG9mIGNvdXJzZSwgdGhlIGNsYXNzaWZpZXIgZm9yIHRoaXMgcmVzdWx0IHNheXMgSW93YSBjb21lcyBvdXQgb24gdG9wLg0KDQpTbyBvdXIgZmluYWwgdGFsbHkgaXMgVGV4YXMgZ290IHRocmVlIHZvdGVzLCBJb3dhIGdvdCB0d28gdm90ZXMsIEZsb3JpZGEgZ290IG9uZSB2b3RlLCBhbmQgQ2FsaWZvcm5pYSBoYXMgemVyby4gQW5kIHdlIGNhbGwgdGhlc2Ugdm90ZXMsIGJ1dCB3ZSdyZSBqdXN0IHRhbGx5aW5nIHRoZSB3aW5uZXIgb2YgZWFjaCBjbGFzc2lmaWVyLiBTbyB3ZSBjYW4gc3VtIHRoZXNlIHVwIGFuZCB3ZSdsbCBnZXQgdGhlc2UgcmVzdWx0cy4gQnV0IHdoYXQgY2FuIGhhcHBlbiBpcyBhIHN0YXRlLCBvciBpbiB0aGlzIGNhc2UsIGEgY2xhc3NpZmllciBjYW4gYWN0dWFsbHkgbG9zZSB0aGUgaGVhZC10by1oZWFkIGFuZCB3aW4gdGhlIG92ZXJhbGwgY2xhc3NpZmllci0tDQoNCndoaWNoIGNhbiBzZWVtIHN0cmFuZ2UuIFNvIHJlbWVtYmVyLCBiZWNhdXNlIHRoaXMgaXMgYSBzdW1tYXJ5IG9mIG91dHB1dHMgYXQgZWFjaCBoZWFkLXRvLWhlYWQsIHlvdSBjYW4gZ2V0IGRpZmZlcmVudCByZXN1bHRzLiBTbyBvbmUgb2YgdGhlIG90aGVyIHByb2JsZW1zIGlzIHlvdSBjYW4gZW5kIHVwIHdpdGggdGllcywgd2hlcmUgZXZlcnlib2R5IGVuZHMgdXAgaW4gYSB0aWUuIEl0J3MgcmFyZSwgYnV0IGl0IGNhbiBoYXBwZW4uDQoNClNvIGhlcmUgd2UndmUgZ290IHRocmVlIHZvdGVzLS0NCg0Kb3IgdHdvIHZvdGVzIGZvciB0aHJlZSBzdGF0ZXMgYW5kIHNpeCBjbGFzc2lmaWVycy4gU28gd2UgaGF2ZSBhIHRpZS4gVGhlcmUncyBubyB3YXkgdG8gYnJlYWsgdGhhdCB0aWUgc3RhdGlzdGljYWxseSBpbiBob3cgdGhpcyBpcyBzZXQgdXAuIFNvIHdlIGhhdmUgdHdvIG1ldGhvZHMgdG8gYXR0ZW1wdCBtdWx0aWNsYXNzLg0KDQpOZWl0aGVyIG9uZSBpcyBwZXJmZWN0LiBSZW1lbWJlciB0aGF0IHRoZSBvbmUtdmVyc3VzLW9uZSBjYW4gaGF2ZSB0aGVzZSB0aWVzLiBBbmQgdGhlIG9uZS12ZXJzdXMtcmVzdCBvciB0aGUgb25lLXZlcnN1cy1hbGwgY2FuIHN1ZmZlciBmcm9tIHVuYmFsYW5jZWQgY2xhc3NpZmllcnMuIEFnYWluLCB3ZSd2ZSBnb3QgdGhhdCBpZGVhIG9mIGNsYXNzL25vdCB0aGUgY2xhc3MuDQoNCkFuZCBhcyB0aGUgbnVtYmVyIG9mIGNsYXNzZXMgaW5jcmVhc2VzLCB3ZSBoYXZlIHRoaXMgYmlhcyB0b3dhcmRzIHRoZSBuZWdhdGl2ZSBjbGFzcywganVzdCBiZWNhdXNlIG9mIHRoZSBudW1iZXIgb2Ygc2FtcGxlcyB0aGF0IHRoZSBjbGFzc2VzIGFyZSBnb2luZyB0byBzZWUuIFNvIGJvdGggb2YgdGhlc2UgaW1wbGVtZW50IHdoYXQgSSBsaWtlIHRvIGNhbGwgInVuZGVyIHRoZSBob29kLiIgV2UncmUgbm90IGdvaW5nIHRvIHNldCB0aGVzZSB1cCBpbmRpdmlkdWFsbHkuIFdoYXQgd2UnbGwgZG8gaXMgd2UnbGwgZ28gdG8gc2tsZWFybiBhbmQgd2UnbGwgc2F5IEkgd291bGQgbGlrZSBhIG9uZS12LW9uZSBtb2RlbCBzaXR1YXRpb24uDQoNCkhlcmUncyBteSBkYXRhLS0NCg0KcnVuLiBPciBJIGhhdmUgYSBvbmUtdi1yLiBJIGhhdmUgYSBvbmUtdmVyc3VzLWFsbCBtZXRob2QgbXVsdGljbGFzcy4gSGVyZSdzIG15IGRhdGEtLQ0KDQpzZXQgaXQgdXAuIFNvIGl0IHRha2VzIHBsYWNlIGJlaGluZCB0aGUgc2NlbmVzIHRoYXQgaXQncyB3ZWxsLWVzdGFibGlzaGVkIGhvdyB0aGVzZSBhcmUgcnVuLiBBbmQgc28geW91IGdldCB5b3VyIGZpbmFsIG91dHB1dHMuIFNvIG9uY2UgYWdhaW4sIGl0IGFsbG93cyB1cyB0byBmb2N1cyBvbiB3aGF0J3MgZ29pbmcgb24gd2l0aCB0aGUgcHJvYmxlbSByYXRoZXIgdGhhbiBpbXBsZW1lbnRpbmcgdGhlIHNvZnR3YXJlIHNvbHV0aW9uLg0KDQpUaGUgc29mdHdhcmUgc29sdXRpb24gaXMgdGhlcmUuIEl0J3MgZm9yIHVzIHRvIHVzZSB0aGUgYXBwcm9wcmlhdGUgc29mdHdhcmUgY29ycmVjdGx5LCBidXQgaXQncyBhbHJlYWR5IGJlZW4gb3B0aW1pemVkIHNvIHdlIGNhbiBnZXQgdGhlIGJlc3QgcGVyZm9ybWFuY2UgYW5kIGxlc3Mgd29ycnlpbmcgYWJvdXQgdGhlIHNwZWNpZmljcyBvZiB0aGUgY29kaW5nIGFuZCBtb3JlIGFib3V0IHRoZSBwcm9ibGVtIGl0c2VsZi4NCg0KDQo3LiAgZGVtb25zdHJhdGluZyBvZiBsb2dpc3RpYyByZWdyZXNzaW8sIHBhcnQgMQ0KDQpXZWxjb21lIHRvIG91ciBkZW1vbnN0cmF0aW9uIG9mIGxvZ2lzdGljIHJlZ3Jlc3Npb24uIFNvIHRvIGdldCBzdGFydGVkLCB3ZSdsbCBkbyBvdXIgdHJhZGl0aW9uYWwgaW1wb3J0cy4gQW5kIG5vdyB3aGF0IHdlIHdhbnQgdG8gZG8gaXMgd2Ugd2FudCB0byBmaW5kIGEgYmluYXJ5IGNsYXNzaWZpY2F0aW9uIHNldC4gVG8gcHJhY3RpY2UsIFNrbGVhcm4gaGFzIGEgbnVtYmVyIG9mIGRhdGEgc2V0cy4NCg0KWW91IGNhbiBhbHdheXMgc2Nyb2xsIHRocm91Z2ggdGhlbS4gQW5kIEkndmUgY2hvc2VuIG9uZSB0byBzdGFydCB3aXRoIHRoYXQgZ2V0cyB1cyBzdGFydGVkIHdpdGggb3VyIGJpbmFyeSBjbGFzc2lmaWNhdGlvbi4gU28gd2UncmUgZ29pbmcgdG8gYmUgdGFraW5nIGEgbG9vayBhdCB0aGUgYnJlYXN0IGNhbmNlciBkYXRhIHNldC4gTGV0J3MgZ28gYWhlYWQgYW5kIGRvIG91ciBmaXJzdCB0YWIuDQoNCkFzIGFsd2F5cywgbGV0J3MgdGFrZSBhIGxvb2sgYXQgb3VyIGRhdGEuIFNvIHlvdSBjYW4gc2VlIHRoaXMgaXMgbm90IGFjdHVhbGx5IGEgZGF0YSBmcmFtZS4gV2hhdCB3ZSd2ZSBnb3QgaGVyZSBpcyBhY3R1YWxseSBhIGRpY3Rpb25hcnksIHdoZXJlIHdlJ3ZlIGdvdCBvdXIgZGF0YSBhbmQgd2UndmUgZ290IG91ciB0YXJnZXRzLiBBbmQgdGhlbiB3ZSd2ZSBnb3QgYWxsIHRoZSBpbmZvcm1hdGlvbiB3ZSBuZWVkLg0KDQpXZSBqdXN0IGhhdmUgdG8gcHV0IGl0IGludG8gYSBkYXRhIGZyYW1lLiBUaGF0J3Mgbm90IHRvbyBoYXJkLiBBbmQgdGhpcyBpcyB0aGUgdHJhZGl0aW9uYWwgd2F5IHRoYXQgZGF0YSBzZXRzIGFyZSBzdG9yZWQgaW4gU2tsZWFybi4gU28gaWYgd2UncmUgbm90IGZhbWlsaWFyIHdpdGggZGljdGlvbmFyaWVzLCB3ZSdyZSBhYm91dCB0byBnZXQgYSBzaG9ydCBsZXNzb24uDQoNClNvIHlvdSBjYW4gc2VlIHRoaXMgaXMgb3VyIGFjdHVhbCBhcnJheSBvZiBkYXRhLiBTbyBsZXQncyBzdGFydCB0byBjcmVhdGUgYSBkYXRhIGZyYW1lLiBBbmQgSSdsbCBqdXN0IGNhbGwgaXQgY2FuY2VyIGRhdGEgZnJhbWUuIEFuZCBmb3IgYnJldml0eSwgdGhpcyBpcyB0aGUgZGF0YSB0aGF0J3MgZ29pbmcgdG8gZ28gaW4uDQoNClNvIHdlJ3ZlIGdvdCBvdXIgaW5kZXhlcy4gTm93IHlvdSdsbCBub3RpY2Ugd2UgZG9uJ3QgaGF2ZSBhbnkgY29sdW1uIG5hbWVzLiBCdXQgdGhlIGNvbHVtbiBuYW1lcyB3ZXJlIGFjdHVhbGx5IHRoZXJlIGluIHRoZSByYXcgZGF0YS4gTGV0J3MgdGFrZSBhIHF1aWNrIGxvb2suDQoNClNvIHdlJ2xsIGhhdmUgdG8gcmVtZW1iZXIgdG8gYWRkIG91ciB0YXJnZXRzIGhlcmUuIEJ1dCBoZXJlJ3MgdGhlIGRlc2NyaXB0aW9uIG9mIGFsbCB0aGUgZGlmZmVyZW50IG5hbWVzLiBTbyBsZXQncyBzZWUgaWYgd2UgY2FuIHB1bGwgdXAgdGhvc2UgZGVzY3JpcHRpb25zLiBTbyB0aGVzZSBhcmUgYWxsIHRoZSBkZXNjcmlwdGlvbnMuDQoNCkFuZCBsZXQncyBzZWUgaWYgd2UgY2FuIHB1bGwgb3V0IHRoZSBuYW1lcyBmb3IgdGhlc2UgcmVhbGx5IHF1aWNrbHkuIFNvIHRoaXMgaXMgZ29pbmcgdG8gdGFrZSB1cyBhIGxpdHRsZSBiaXQuIFNvIEkgbWF5IGN1dCB0aGUgdmlkZW8gYXQgdGhpcyBwb2ludCB3aGlsZSBJIGNvbWUgdXAgd2l0aCBhIHF1aWNrIHdheSB0byBwdXQgdGhlc2UgdG9nZXRoZXIuIFdlIGNhbiBzZWUgdGhhdCBpdCdzIG5vdCBleGFjdGx5IGVhc3kgdG8gcHVsbCB0aGVzZSBvdXQgYW5kIHdvdWxkIHRha2UgYSBsaXR0bGUgYml0IG9mIGZvcm1hdHRpbmcuDQoNCkl0J3Mgbm90IGltcG9zc2libGUuIEJ1dCBmb3IgdGhlIG1lYW50aW1lLCBJJ20ganVzdCBnb2luZyB0byBtb3ZlIG9uIHdpdGhvdXQgaGF2aW5nIG15IGFjdHVhbCBuYW1lcy4gV2Uga25vdyB0aGlzIGRhdGEgc2V0IGlzIHdlbGwgZm9ybWF0dGVkIGJlY2F1c2UgaXQncyBhIHRveSBkYXRhIHNldCBhcyB3ZWxsIGFzIHlvdSBjYW4gcmVhZCB0aGUgc3VtbWFyeSBvbiBTa2xlYXJuLiBTbyB3ZSdsbCBqdXN0IGNvbnRpbnVlLg0KDQpCdXQgd2UgZG9uJ3QgbmVlZCB0byBmb3JnZXQgdGhhdCB3ZSBhY3R1YWxseSBoYXZlIHRvIGFkZCB0aGUgdGFyZ2V0IGFzIHdlbGwuIFNvIGxldCdzIGdvIGFoZWFkIGFuZCBhZGQgdGhhdCB0byBvdXIgZGF0YSBmcmFtZS4gU28gbGV0J3MgbG9vayBhdCBvdXIgZmluYWwgZGF0YS4gT29wcy4NCg0KVGhpcyBpcyB3aHkgSSB0YWIgY29tcGxldGUgcXVpdGUgYSBiaXQuIFNvIHdlIGNhbiBzZWUgd2UndmUgZ290IG91ciB2YWx1ZXMuIEFuZCBpZiB3ZSB3YW50ZWQgdG8gbG9vayB1cCB3aGF0IHRob3NlIHZhbHVlcyBhcmUsIHdlIGNvdWxkLiBUaGVuIHdlIHNlZSB3ZSd2ZSBnb3QgdGFyZ2V0cyBvZiAwJ2EgYW5kIDEncy4NCg0KU28gd2UndmUgZ290IG91ciBkYXRhLiBJdCdzIGJpbmFyeSBjbGFzc2lmaWVkLiBXaGF0IHdlIG5lZWQgdG8gZG8gaXMgbGV0J3Mgc3RhcnQgb3VyIGJhc2ljIGxvZ2lzdGljIHJlZ3Jlc3Npb24uIE5vdywgeW91J2xsIG5vdGljZSBhdCB0aGlzIHBvaW50LCBJIGhhdmUgbm90IHNjYWxlZCB0aGUgZGF0YS4NCg0KQnV0IHdlIGNhbiBzdGlsbCBnZXQgZmFpcmx5IGdvb2QgcmVzdWx0cyB3aXRob3V0IHNjYWxpbmcgdGhlIGRhdGEuIEFuZCBieSBub3Qgc2NhbGluZyB0aGUgZGF0YSBpbml0aWFsbHksIHdlIGNhbiBwZXJoYXBzIGNvbXBhcmUgYW5kIGNvbnRyYXN0IHRoZSBpbXBvcnRhbmNlIG9mIHNjYWxpbmcgdGhlIGRhdGEuIEZvcnR1bmF0ZWx5LCBkb2luZyBvdXIgbG9naXN0aWMgcmVncmVzc2lvbiBpcyBhY3R1YWxseSBnb2luZyB0byBiZSBmYWlybHkgcXVpY2suIE5vdywgdGhpcyBpcyB0aGUgZmlyc3QgdGltZSB3ZSd2ZSBlbmNvdW50ZXJlZCBvdXIgU2tsZWFybi4NCg0KWW91IGNhbiBub3RpY2UgdGhhdCB0aGVyZSBhcmUgbXVsdGlwbGUgdmVyc2lvbnMgb2YgbG9naXN0aWMgcmVncmVzc2lvbi4gTm93IHdlIGFsd2F5cyBsaWtlIHRvIGRvIGNyb3NzLXZhbGlkYXRpb24uIEJ1dCB5b3UgY2FuIHVzZSByZWd1bGFyIGxvZ2lzdGljIHJlZ3Jlc3Npb24gYW5kIGEgY3Jvc3MtdmFsaWRhdGlvbiBmcm9tIG90aGVyIHBhcnRzIG9mIFNrbGVhcm4uIFRoZSBMb2dpc3RpYyBSZWdyZXNzaW9uIENWLCBiZWNhdXNlIGl0J3Mgc3VjaCBnb29kIHByYWN0aWNlLCBoYXMgaXQgYnVpbHQgaW4uDQoNCkF0IHRoaXMgcG9pbnQsIGxldCdzIGdvIGFoZWFkIGFuZCB0YWtlIGEgbG9vayBhdCBvdXIgTG9naXN0aWMgUmVncmVzc2lvbiBDVi4gU28gSSd2ZSBpbXBvcnRlZCB0aGlzIGNsYXNzLiBBbmQgd2UncmUgZ29pbmcgdG8gdXNlIHRoaXMgYXMgb3VyIG1vZGVsLiBOb3csIGEgbGl0dGxlIGJpdCBvZiBvYmplY3Qtb3JpZW50ZWQgcHJvZ3JhbW1pbmcsIExvZ2lzdGljIFJlZ3Jlc3Npb24gQ1YgaXMgYSBjbGFzcy4NCg0KU28gd2UndmUganVzdCBpbXBvcnRlZCB0aGlzIGNsYXNzLiBBbmQgbm93IHdoYXQgd2UgbmVlZCB0byBkbyBpcyB3ZSdyZSBnb2luZyB0byBjcmVhdGUgYSBtb2RlbC4gU28gb3VyIG1vZGVsIGlzIGFuIGluc3RhbmNlIG9mIHRoZSBMb2dpc3RpYyBSZWdyZXNzaW9uIGNsYXNzLiBOb3csIHdlIGNhbiBhY2NlcHQgdGhlIGRlZmF1bHQgcGFyYW1ldGVycyBieSBqdXN0IHVzaW5nIGVtcHR5IHBhcmVudGhlc2VzLg0KDQpJZiB5b3UncmUgaW50ZXJlc3RlZCBpbiBhbGwgdGhlIG9wdGlvbnMsIHlvdSBtYXkgd2FudCB0byB0YWtlIGEgbG9vayBhdCB0aGUgU2tsZWFybiBkb2N1bWVudGF0aW9uLiBTbyB3ZSBtYXkgZ2V0IHNvbWUgbW9ybmluZ3MgYXMgd2Ugc3RhcnQgdGhpcy4gQnV0IHRoaXMgaXMgb3VyIGJhc2UgbG9naXN0aWMgcmVncmVzc2lvbiBjcm9zcy12YWxpZGF0aW9uLiBMZXQncyBnbyBhaGVhZCBhbmQgaW5zdGFudGlhdGUgaXQuDQoNClNvIG5vdyB3ZSd2ZSBnb3QgYSBtb2RlbCwgbm93IGl0J3MgdGltZSBmb3IgdXMgdG8gZml0IG91ciBtb2RlbC4gU28gdG8gZml0IG91ciBtb2RlbCwgd2UgbmVlZCBib3RoIHRhcmdldHMgYW5kIGRhdGEuIEJ1dCB0aGF0IHdhcyBpbiB0aGUgZGF0YSBmcmFtZS4gV2UndmUgYWxyZWFkeSBnb3QgdGhhdC4NCg0KU28gSSd2ZSBnb3QgbXkgZGF0YSBmcmFtZS4gQnV0IHJlbWVtYmVyLCB0aGUgZGF0YSBmcmFtZSBjb250YWlucyBib3RoIHRoZSB0YXJnZXRzIGFuZCB0aGUgZGF0YS4gU28gd2UgbmVlZCBhIHdheSB0byBzcGxpdCB0aGlzIHVwLiBJIGNhbiBkbyB0aGF0IHdpdGggcGFuZGFzIGxvY2F0aW9uLg0KDQpPciBJIGNhbiBhY3R1YWxseSBzcGxpdCB0aGVzZSB1cC4gQW5kIGVpdGhlciB3YXksIHdlIHdpbGwgZ2V0LCBlc3NlbnRpYWxseSwgb3VyIHggZGF0YSBhbmQgb3VyIHkgZGF0YS4gU28gd2hhdCBJIGNhbiBkbyBmb3IgdGhlIHkgZGF0YSwgd2Uga25vdyB0aGF0IHRoYXQncyB0aGUgdGFyZ2V0LiBOb3csIHRoaXMgbWF5IGxvb2sgYSBsaXR0bGUgY3JhenkuDQoNCkJ1dCB3aGF0IEknbSBkb2luZyBpcyBJJ20gY3JlYXRpbmcgYW4gb2JqZWN0IHdoZXJlIEkgZHJvcCB0aGUgdGFyZ2V0IGNvbHVtbi4gQW5kIHRoZW4gaGVyZSBhcmUgbXkgdGFyZ2V0cy0tDQoNCnRoZSBhY3R1YWwgdGFyZ2V0IGNvbHVtbi4gWW91IG1heSBiZSBhZnJhaWQgdGhhdCB3aGVuIEkgZHJvcCB0aGUgdGFyZ2V0LCBJJ20gZ29pbmcgdG8gZHJvcCBpdCBmcm9tIHRoZSBkYXRhIGZyYW1lIGNvbXBsZXRlbHkuIEhvd2V2ZXIsIGluIHRoaXMgY2FzZSwgYmVjYXVzZSBJJ20gbm90IGFzc2lnbmluZyB0aGUgcmVzdWx0IHRvIGFueXRoaW5nLCB0aGlzIGp1c3QgcmV0dXJucyBhbiBvYmplY3QuIEl0IGhhcyBubyBuYW1lLg0KDQpTbyB0aGlzIGlzIGVzc2VudGlhbGx5IG91ciB4IGRhdGEsIHdoaWxlIHRoaXMgaXMgb3VyIHkgZGF0YS4gV2UnbGwgcHJvYmFibHkgZ2V0IGEgd2FybmluZyBiZWNhdXNlIHdlIGRpZG4ndCBzcGVjaWZ5IGhvdyBtYW55IGZvbGRzIGluIG91ciBjcm9zcy12YWxpZGF0aW9uLiBCdXQgbGV0J3MgZ28gYWhlYWQgYW5kIHNlZSBpZiB0aGlzIHdvcmtzLiBXZWxsLCB3ZSBmb3VuZCBub3QgaW4gdGhlIGF4aXMuDQoNClNvIEkgcHJvYmFibHkgbWlzc3BlbGxlZCB0aGlzLiBBbmQgbGV0J3Mgc2VlIGlmIHdlIGNhbiBmaWd1cmUgb3V0LS0NCg0KaG1tLCB0aGlzIGlzIGludGVyZXN0aW5nLiBTbyB0aGlzIGlzIG9uZSBvZiBteSBtb3N0IGNvbW1vbiBtaXN0YWtlcy4gSSBmb3JnZXQgdG8gdGVsbCBpdCBpZiBJJ20gZHJvcHBpbmcgYSBjb2x1bW4gb3IgYSByb3cuIE9LLCBzbyB3ZSd2ZSBnb3Qgc29tZSB3YXJuaW5ncyBoZXJlLg0KDQpBbmQgdGhpcyBpcyBvdXIgcmVzdWx0cy4gQW5kIHdoaWxlIHRoZXNlIGFyZSB3YXJuaW5ncywgdGhleSdyZSBub3QgdG9vIHRlcnJpYmx5IG9mIGNvbmNlcm4gd2l0aCB1cy4gQW5kIGl0J3MgdGVsbGluZyB1cyB3ZSd2ZSByZWFjaGVkIG91ciBpdGVyYXRpb24gbGltaXRzLiBJbiBvdGhlciB3b3JkcywgaXRzIGl0ZXJhdGlvbi4NCg0KQnV0IGl0IG1heSBub3QgaGF2ZSBmaW5hbGx5IGZvdW5kIGEgc29sdXRpb24uIFNvIHdlIGZpbmFsbHkgZ290IG91ciBkb25lLiBBbmQgd2UndmUgZ290IG91ciByZXN1bHRzLiBTbyB3ZSBkb24ndCBoYXZlIGFueXRoaW5nIHByaW50ZWQuDQoNCkhvdyBkbyB3ZSBzZWUgb3VyIHJlc3VsdHM/IFdlbGwsIHRoZXkncmUgYWN0dWFsbHkgc3RvcmVkIGFzIHBhcnQgb2YgdGhlIG1vZGVsLiBOb3csIHlvdSdsbCBub3RpY2UgSSBhY3R1YWxseSBwdXQtLQ0KDQpJIGRpZCB0aGF0IHF1aXRlIHF1aWNrbHkuIExldCBtZSBzdG9wIGFuZCBnbyBiYWNrLiBCZWNhdXNlIHRoZXkncmUgc3RvcmVkIGFzIHBhcnQgb2YgdGhlIG1vZGVsLCB0aGlzIG1vZGVsIGlzIGFuIG9iamVjdCBhbmQgaXQgaGFzIGF0dHJpYnV0ZXMgd2l0aCBpdC4gVGhvc2UgYXR0cmlidXRlcyBjYW4gYmUgYWNjZXNzZWQgdGhyb3VnaCB2YXJpb3VzIG1ldGhvZHMuDQoNCkFnYWluLCB3ZSdyZSBpbnRvIGEgbGl0dGxlIGJpdCBvZiBvYmplY3Qtb3JpZW50ZWQgcHJvZ3JhbW1pbmcuIEJ1dCB0aGUga2V5IHRoaW5nIGlzIHdlIGNhbiBhY2Nlc3MgdGhvc2UgYXR0cmlidXRlcy4gV2hlbiBJIGhpdCB0aGUgcGVyaW9kIGhlcmUsIHRoaXMgbWVhbnMgSSB3YW50IG9uZSBvZiB0aGUgYXR0cmlidXRlcyBvciBtZXRob2RzIG9mIG15IG1vZGVsLiBXaGF0IEkgY2FuIGRvIGlzIG5vdyBJJ20gZ29pbmcgdG8gaGl0IFRhYi4NCg0KQW5kIHRoaXMgd2lsbCBicmluZyB1cCBhIGxpc3Qgb2YgYXR0cmlidXRlcy4gU28gdGhpcyB3aWxsIHRlbGwgdXMgYWxsIHRoZSBkaWZmZXJlbnQgdGhpbmdzIHdlIHdhbnQgdG8ga25vdy4gRmlyc3Qgb2YgYWxsLCB0aGUgQywgdGhhdCdzIGdvaW5nIHRvIGJlIG91ciByZWd1bGFyaXphdGlvbi4gV2UgbWF5IG9yIG1heSBub3QgaGF2ZSB0YWxrZWQgYWJvdXQgcmVndWxhcml6YXRpb24gYXQgdGhpcyBwb2ludC4NCg0KQnV0IGFuIGltcG9ydGFudCByZW1pbmRlciB0aGF0IHJlZ3VsYXJpemF0aW9uIGlzIGtleSB0byBhbnkgbW9kZWwuIE9uZSBvZiB0aGUgbW9zdCBpbXBvcnRhbnQgdGhpbmdzIGlzLCBvZiBjb3Vyc2UsIG91ciBjb2VmZmljaWVudHMgb3Igb3VyIHNsb3Blcy4gU28gbGV0J3MgdGFrZSBhIGxvb2sgYXQgd2hhdCBvdXIgY29lZmZpY2llbnRzIGFyZS4gVGhpcyBpcyBnb2luZyB0byB0ZWxsIHVzIGFsbCB0aGUgY29lZmZpY2llbnRzIG9yIHNsb3BlcyBmb3IgZWFjaCBjb2x1bW5zLg0KDQpOb3csIGF0IHRoaXMgcG9pbnQsIG91ciBjb2x1bW5zIGFyZSB1bm5hbWVkLiBCdXQgd2UgY2FuIGFjdHVhbGx5IHRpZSB0aGVzZSBiYWNrIGJlY2F1c2UgdGhlc2UgYXJlIGluIG9yZGVyLiBUaGlzIGlzIHRoZSBjb2VmZmljaWVudCBmb3IgdGhlIGZpcnN0LCBvciB6ZXJvdGgsIGNvbHVtbiwgdGhlIHNlY29uZCBjb2x1bW4sIGFuZCBzbyBvbiBhbmQgc28gZm9ydGguIE9idmlvdXNseSwgdGhlIGxhcmdlciBpbiBhYnNvbHV0ZSB2YWx1ZSB0aGUgY29sdW1uLCB0aGUgbW9yZSBpbXBvcnRhbnQgaXQgaXMuDQoNCkJ1dCBpZiB5b3UgcmVtZW1iZXIsIHdlIGRpZG4ndCBub3JtYWxpemUgdGhhdCBkYXRhLiBTbyB0aGUgcmFuZ2Ugb2YgdGhlIGRhdGEgY2FuIGhhdmUgYW4gaW1wYWN0IG9uIHRoZXNlIGNvZWZmaWNpZW50cy4gU28gdGhlIGltcG9ydGFuY2UgaXMgc29tZXdoYXQgaGlkZGVuLiBBbmQgd2UnbGwgc2VlIHRoYXQgd2hlbiB3ZSBub3JtYWxpemUgb3VyIGRhdGEgaW4ganVzdCBhIG1vbWVudC4NCg0KQnV0IHdlJ3ZlIGdvdCBvdXIgY29lZmZpY2llbnRzIG91dC4gTGV0J3Mgc2VlIGlmIHRoZXJlJ3MgYW55dGhpbmcgZWxzZS4gTGV0J3MgdGFrZSBhIGxvb2suIElzIHRoZSBzbG9wZSBwYXJ0IG9mIHRoZXNlIGNvZWZmaWNpZW50cyBvciBub3Q/DQoNCk9uZSBvZiB0aGUgcXVpY2sgd2F5cyB3ZSBjYW4gbG9vayBhdCB0aGF0IGlzIGxldCdzIHRha2UgYSBsb29rIGF0IG91ciBkYXRhIGZyYW1lLiBBbmQgcmVtZW1iZXIsIHdlJ3ZlIGdvdCBhbiBleHRyYSBjb2x1bW4gaW4gdGhlcmUuIFRoYXQncyB0aGUgdGFyZ2V0LiBTbyB3ZSBzZWUgdGhhdCB0aGVyZSdzIDMxIGZlYXR1cmVzLg0KDQpMZXQncyB0YWtlIGEgbG9vayBhdCBob3cgbWFueSBjb2VmZmljaWVudHMgdGhlcmUgYXJlLiBTbyB5b3UnbGwgbm90aWNlIHRoZXJlIGFyZSAzMCBjb2VmZmljaWVudHMuIE5vdywgdGhpcyBzaGFwZSB3YXMgMzEuIEJ1dCBkb24ndCBmb3JnZXQgd2UndmUgZ290IHRoZSB0YXJnZXRzIGluIHRoZXJlLg0KDQpTbyB3ZSd2ZSBnb3QgYSBjb2VmZmljaWVudCBmb3IgZWFjaCBjb2x1bW4sIHdoaWNoIG1lYW5zIHRoZSB5LWludGVyY2VwdCBpcyBub3QgYWN0dWFsbHkgaW5jbHVkZWQgaW4gdGhlc2UgY29lZmZpY2llbnRzLiBJdCdzIGFsd2F5cyBnb29kIHRvIGNoZWNrIHRvIHNlZSB3aGV0aGVyIHRoZXkgY29uc2lkZXIgdGhlIGludGVyY2VwdCBpbmRlcGVuZGVudCBvZiB0aGUgY29lZmZpY2llbnRzLiBCZWNhdXNlIG1hbnkgdGltZXMsIHdlIGdyb3VwIGl0IGluLiBBbmQgYmVjYXVzZSBkYXRhIHNjaWVuY2UgaXMgY29uZ2xvbWVyYXRlIGFuZCBicmluZ3MgaW4gbWV0aG9kcyBmcm9tIG11bHRpcGxlIHNjaWVuY2VzLCB5b3UgY2FuIG5ldmVyIGJlIHN1cmUuDQoNClNvIGl0J3MgYWx3YXlzIGdvb2QgdG8gY2hlY2sgd2hldGhlciBvciBub3QgeW91ciBhc3N1bXB0aW9ucyBhcmUgY29ycmVjdC4gSSB3b3VsZCBoYXZlIGluaXRpYWxseSBhc3N1bWVkIHRoYXQgdGhlIGludGVyY2VwdCB3YXMgcGFydCBvZiB0aGVzZSBjb2VmZmljaWVudHMuIEJ1dCBpbiB0aGlzIGNhc2UsIGl0J3Mgbm90LiBMZXQncyB0YWtlIGEgbG9vayBhdCBzb21lIG9mIHRoZSBvdGhlciB0aGluZ3MgdGhhdCBhcmUgaW52b2x2ZWQgaGVyZS4NCg0KU28gYXMgd2Ugc2F3LCB3ZSd2ZSBnb3QgdGhlIGRpZmZlcmVudCB2YWx1ZXMuIFdlIHNlZSBoZXJlIGlzIHRoZSBpbnRlcmNlcHQuIEFuZCB3ZSd2ZSBnb3QgYWxsIHRoZSBvdGhlciBwYXJhbWV0ZXJzLiBXaGF0IHdlIGRpZG4ndCBkby0tDQoNCm15IGFwb2xvZ2llcyBmb3IgYWNjaWRlbnRhbGx5IGNsaWNraW5nIG9uIHRoYXQtLQ0KDQp3YXMgd2UgZGlkbid0IGtub3cgaG93IG1hbnkgY3Jvc3MtdmFsaWRhdGlvbnMgd2UgZGlkLiBTbyBsZXQncyBzZWUgaWYgd2UgY2FuIGZpbmQgdGhhdCBvdXQgbm93LiBBbmQgaXQncyBhY3R1YWxseSBub3QgdGVsbGluZyB1cy4gU28gd2UgY2FuJ3QgdW5kZXJzdGFuZCBhdCB0aGlzIHBvaW50IHdoYXQgd2FzIHRoZSBkZWZhdWx0IGNyb3NzLXZhbGlkYXRpb24uDQoNClRoaXMgaXMgc29tZXRpbWVzIHdoZW4gd2UgbWlnaHQgaGF2ZSB0byBnbyB0byB0aGUgZG9jdW1lbnRhdGlvbi4gQnV0IGxldCdzIHNlZSBpZiB3ZSBjYW4gcG9zc2libHkgY2hhbmdlIHRoYXQgYmVjYXVzZSB3ZSBrbm93IHRoaXMgaXMgYSB2YWx1ZSBpbiBvdXIgbG9naXN0aWMgcmVncmVzc2lvbi4gSWYgd2UgcmVtZW1iZXIgb3VyIGZpdCBtZXRob2Qgb3IgcG9zc2libHkgb3VyIGluc3RhbnRpYXRpb24sIHdlIGNhbiBwb3NzaWJseSBjaGFuZ2UgdGhlIHZhbHVlIGZvciBDVi4gU28gbGV0J3MgZ28gYmFjay4NCg0KQW5kIEknbSBnb2luZyB0byBjcmVhdGUgYSBuZXcgbW9kZWwuIEFuZCwgb2YgY291cnNlLCBpdCdzIGFuIGluc3RhbmNlIG9mIHRoZSBMb2dpc3RpYyBSZWdyZXNzaW9uIENWLiBBbmQgbGV0J3Mgc2VlIGlmIHdlIGNhbiBmaWd1cmUgb3V0IGhvdyB0byBjaGFuZ2UgdGhlIG51bWJlciBvZiBmb2xkcy4gTm93LCB3ZSdyZSBub3Qgc2VlaW5nIGFueXRoaW5nLg0KDQpJIHByZXNzIFRhYiB0byBjb21lIHVwLiBBbmQgc28gaXQncyBub3Qgc2hvd2luZyB1cy4gT25lIG9mIHRoZSB0aGluZ3MgSSBjYW4gZG8gaXMtLQ0KDQpJIGtub3cgdGhhdCB0eXBpY2FsbHkgb25lIG9mIHRoZSB0aGluZ3MgaXMgbiBmb2xkcy4gTm93LCBJJ3ZlIGdvdCBuIGpvYnMuIEFuZCBhdCB0aGlzIHBvaW50LCBiZWNhdXNlIEkgZG9uJ3QgdXNlIGxvZ2lzdGljIHJlZ3Jlc3Npb24gYW5kIG5vdCBhIGxvdCwgSSBtYXkgaGF2ZSB0byBnbyBiYWNrIGFuZCByZWZyZXNoIG15IG1lbW9yeSB3aXRoIHRoZSBkb2N1bWVudGF0aW9uLiBTbyBsZXQncyBwcm9iYWJseSBnbyBhbmQgZG8gdGhhdC4NCg0KQW5kIGxldCdzIHRha2UgYSBsb29rIGF0IHRoZSBTa2xlYXJuIGRvY3VtZW50YXRpb24uIEl0J3MgYSBsaXR0bGUgYml0IGhhcmQgdG8gcmVtZW1iZXIgYWxsIHRoZSB0aGluZ3MuIFNvIGRvbid0IGJlIGFzaGFtZWQgaWYgeW91IGNhbid0IHJlbWVtYmVyIHRoZXNlIHRoaW5ncyBvZmYgdGhlIHRvcCBvZiB5b3VyIGhlYWQuIFNvIGxldCdzIGNyZWF0ZSBhIG5ldyB0YWIuDQoNCkFuZCBJJ20gZ29pbmcgdG8gZ28gYW5kIG9wZW4gdXAgdGhlIFNrbGVhcm4gZG9jdW1lbnRhdGlvbi4NCg0KDQo4LiAgRGVtb25zdHJhdGlvbiBvZiBMb2dpc3RpYyBSZWdyZXNzaW9uLCBQYXJ0IElJDQoNCkhlcmUgd2UgYXJlIGF0IHRoZSBTSyBMZWFybiBkb2N1bWVudGF0aW9uLiBXaGF0IHdlJ3JlIGdvaW5nIHRvIGRvIGlzIHdlJ3JlIGdvaW5nIHRvIGxvb2sgZm9yIHRoYXQgbG9naXN0aWMgcmVncmVzc2lvbiBDVi4gTm93IHRoZXJlIGFyZSBhIG51bWJlciBvZiB3YXlzIHRvIGdldCB0aGVyZS4gV2UnbGwganVzdCB0eXBlIHRoYXQgaW4uDQoNCkFuZCB3ZSBzZWUgbG9naXN0aWMgcmVncmVzc2lvbiBDViBwb3AgdXAuIE5vdyB3ZSd2ZSBnb3Qgb3VyIGRvY3VtZW50YXRpb24uIEFuZCB3ZSBjYW4gc2VlIHJpZ2h0IGhlcmUgaG93IHRvIGRlZmluZSB0aGUgZGlmZmVyZW50IGltcG9ydGFudCBwYXJhbWV0ZXJzLiBBbmQgd2Ugc2VlIHRoYXQgd2UgbWF5IGhhdmUgY2F1Z2h0IG91cnNlbHZlcyBhIGxpdHRsZSBiaXQgYmVjYXVzZSB3ZSBzZWUgdGhlIGRlZmF1bHQgaXMgQ1YgZXF1YWxzIG5vbmUuDQoNCkFuZCBzbyB3ZSBoYXZlIHRoaXMgQ1YuIEFuZCB3ZSBsb29rIGRvd24gaGVyZS4gSXQncyB0aGUgY3Jvc3MtdmFsaWRhdGlvbiBnZW5lcmF0b3IuIEFuZCB0aGUgZGVmYXVsdCBiZWluZyBub25lLg0KDQpTbyBldmVuIHRob3VnaCB3ZSBzdGFydGVkIG9mZiBhc3N1bWluZyB3ZSB3ZXJlIGRvaW5nIGNyb3NzLXZhbGlkYXRpb24sIGl0IHR1cm5lZCBvdXQgd2Ugd2VyZW4ndCBkb2luZyBhbnkgYmVjYXVzZSB3ZSBkaWRuJ3QgaGF2ZSBhbiBpbnRlZ2VyLiBTbyB0byBwcm9wZXJseSBkbyBhIGNyb3NzLXZhbGlkYXRpb24sIHdlJ2xsIGFjdHVhbGx5IGhhdmUgdG8gYXNzaWduIGFuIGludGVnZXIuIFRoaXMgaXMgd2h5IGl0J3MgaGVscGZ1bCB0byBhbHdheXMgYnJpbmcgdXAgdGhlIGRvY3VtZW50YXRpb24uIEl0J3MgZmFpcmx5IGNsZWFyIGhlcmUgaW4gU0sgTGVhcm4uDQoNCkFuZCBpdCdzIG9uZSBvZiB0aGUgYmVuZWZpdHMgb2YgdGhpcyBwYWNrYWdlIHRvIGdvIHRocm91Z2guIFNvIHdoaWxlIHdlJ3JlIGhlcmUsIHdlIGNhbiB0YWtlIGEgbG9vayBhdCBhbGwgdGhlIGRpZmZlcmVudCBvcHRpb25zIGp1c3QgdG8gcmVtaW5kIG91cnNlbHZlcyB0aGF0IHdlJ3JlIGRvaW5nIHRoaW5ncyB0aGUgY29ycmVjdCB3YXkuIFNvIHdlIHN0YXJ0IHdpdGggb3VyIGNyb3NzLXZhbGlkYXRpb24uIEFuZCBhcyB3ZSBicm93c2UgdGhyb3VnaCB0aGlzLCB3ZSBzZWUgdGhhdCB0aGlzIHdvdWxkIGJlIG91ciByZWd1bGFyaXphdGlvbiwgb3VyIENzLCBhcyBJIG1heSBoYXZlIG1lbnRpb25lZCBiZWZvcmUuDQoNClRoYXQgbWF5IG5vdCBiZSBvYnZpb3VzLiBJJ3ZlIGJlZW4gaGVyZSBiZWZvcmUgc28gSSBrbm93IHRoYXQgQyByZXByZXNlbnRzIHRoZSByZWd1bGFyaXphdGlvbi4gV2UnbGwgc2VlIG91ciBwZW5hbHRpZXMgZG93biBoZXJlLiBXZSdsbCB0YWxrIGFib3V0IHRoYXQgZnJlcXVlbnRseSB0aHJvdWdob3V0IHRoZSBjb3Vyc2UuDQoNCldlJ2xsIHRhbGsgYWJvdXQgb3VyIHNjb3JpbmcuIFNvIHdlIGNhbiBoYXZlIGEgcmVwb3J0IG9uIHRoZSBkaWZmZXJlbnQgc2NvcmluZy4gQW5kIHJpZ2h0IG5vdyBpdCdzIG5vdCB0ZWxsaW5nIHVzIGFueSBzY29yaW5nLiBXZSBzYXcgdGhlIHN0b3BwaW5nIGNyaXRlcmlhLg0KDQpOb3cgeW91IG1heSBhc2sgeW91cnNlbGYsIHdoYXQgZG8geW91IG1lYW4gc3RvcHBpbmcgY3JpdGVyaWE/IEFzIHRoZSBkaWZmZXJlbnQgc29sdmVycyBhcmUgdXNlZCwgdGhleSBzdG9wIHdoZW4gdGhlIGxvc3MgYmVnaW5zIHRvIGRlY3JlYXNlIG9yIHN0b3BzIGRlY3JlYXNpbmcgYSBjZXJ0YWluIGFtb3VudC4gQW5kIHNvIHdlIGRvbid0IHR5cGljYWxseSB1c2UgdGhpcywgYnV0IHRoYXQncyB3aXRoIG91ciB3YXJuaW5ncyB3ZXJlIGFib3V0IGlmIHlvdSBtYXkgaGF2ZSBub3RpY2VkIGFuZCB3ZSdyZSByZWFkaW5nIGFsb25nLiBXZSBoYXZlIHZhcmlvdXMgaXNzdWVzIGZvciBjbGFzcyB3ZWlnaHRzIGFuZCBlZmZpY2llbmNpZXMuDQoNClRoZXNlIGFyZSBtb3JlIGNvbXB1dGF0aW9uLiBTbyBvdXIgbWFpbiB0aGluZyBpcyBsZXQncyBnbyBiYWNrIGFuZCBkbyBvdXIgY3Jvc3MtdmFsaWRhdGlvbi4gU28gSSdtIGdvaW5nIHRvIGNsaWNrIGJhY2suIEFuZCBsZXQncyBkbyBhIGZpdmUtZm9sZCBjcm9zcy12YWxpZGF0aW9uLg0KDQpTbyB3ZSd2ZSBnb3QgdGhpcyBuZXcgbW9kZWwuIEFuZCBsZXQncyBnbyBhaGVhZCBhbmQgZml0IGl0LiBOb3cgSSdtIGdvaW5nIHRvIGdvIGFuZCBzY3JvbGwgdXAuIEFuZCBJJ20ganVzdCBnb2luZyB0byBjb3B5IHdoYXQgSSB1c2VkIGJlZm9yZSwgYmVjYXVzZSBJIGtub3cgaXQgd29ya3MuDQoNClNvIHdlJ3JlIHNlZWluZyBzdGlsbCB3ZSdyZSBnZXR0aW5nIHRoZSBudW1iZXIgb2YgaXRlcmF0aW9ucy4gQnV0IG5vdyB3ZSBzZWUgd2UncmUgc3RpbGwgbm90IGdldHRpbmcgcmVzdWx0cy4gTGV0J3Mgc2VlIGlmIHdlIGNhbiBhZGQgaW4gdGhhdCBzY29yaW5nIG1ldHJpYy4gU28gd2UncmUganVzdCBnb2luZyB0byByZXdyaXRlLCBldmVuIHRob3VnaCB0aGlzIGlzIG91ciBuZXcgbW9kZWwuDQoNCkFuZCBsZXQncyBzZWUgd2hhdCB0aGUgYWNjdXJhY3kgaXMuIFNvIG9uY2UgYWdhaW4sIHdlJ3JlIGp1c3QgZ29pbmcgdG8gdXNlIHRoZSBzYW1lIGZpdCBjb21tYW5kLiBBbmQgd2UgaGF2ZW4ndCBoYWQgYW55dGhpbmcgY29tZSBvdXQuIFNvIGlzIHRoYXQgc3RvcmVkIHNvbWV3aGVyZSB3aXRoaW4gb3VyIG1vZGVsPw0KDQpTbyBsZXQncyB0YWtlIGEgbG9vay4gQWhhLiBXZSd2ZSBnb3Qgc2NvcmVzIGluIGhlcmUuIFNvIHdoYXQgd2UncmUgc2VlaW5nIG9uIHNjb3JlcyBpcyBub3Qgd2hhdCB3ZSBleHBlY3QuDQoNCldlIHNhdyBmb3Igc2NvcmluZyB3ZSBleHBlY3RlZCBhY3R1YWxseSBmaXZlIHZhbHVlcyBvdXQuIFNvIHRoaXMgaXMgc29tZXRoaW5nIHRoYXQncyBub3Qgb3VyIGFjdHVhbCBzY29yZXMgb2Ygb3VyIGNyb3NzLXZhbGlkYXRpb24uIFNvIHdoZW4gd2UgYWN0dWFsbHkgZG8gdGhlIHNjb3JlLCB0aGlzIGlzIGFza2luZyB1cyBmb3Igb3VyIHggYW5kIHkgdmFsdWVzLiBXaGF0IHdlJ3JlIGZpbmRpbmcgaXMgdGhhdCB0aGUgQ1YgbWV0aG9kIGRvZXMgbm90IGFjdHVhbGx5IHJldHVybiB0byB1cyB0aGUgc2NvcmVzIGZvciBvdXIgY3Jvc3MtZm9sZHMuDQoNClNvIHdoYXQgd2UgY2FuIGRvIGlzIHdlIGNhbiBhY3R1YWxseSBtb3ZlIGJhY2sgZWl0aGVyIHRvIGEgcmVndWxhciBsb2dpc3RpYyByZWdyZXNzaW9uIGFuZCB1c2UgdGhlIG1vZGVsIHNlbGVjdGlvbiB0byBkbyBjcm9zcy12YWxpZGF0aW9uLiBIb3dldmVyLCBhdCB0aGlzIHBvaW50LCBsZXQncyBzZWUgd2hhdCBvdXIgc2NvcmUgaXMganVzdCBmb3Igb3VyIGJhc2ljIHggYW5kIHkuIEknbSBqdXN0IGdvaW5nIHRvIHF1aWNrbHkgcHVsbCB0aGVzZS4gQW5kIHlvdSBjYW4gc2VlIHdlJ3ZlIGdvdCA5NyUgYWNjdXJhY3kuDQoNCkhvd2V2ZXIsIHRoZSBhc3R1dGUgYW1vbmcgeW91IHdpbGwgbm90aWNlIHRoaXMgaXMgZGF0YSB0aGF0IHdhcyB1c2VkIHRvIGZpdCB0aGUgbW9kZWwuIFNvIHRoaXMgaXMgYSBiaWFzZWQgZXN0aW1hdGUuIEFuZCB3aGF0IHdlJ3JlIHJlYWxseSBpbnRlcmVzdGVkIGluIGlzIG91ciB1bmJpYXNlZCBlc3RpbWF0ZS4gSXQgZG9lc24ndCBhcHBlYXIgdGhhdCB0aGUgbG9naXN0aWMgcmVncmVzc2lvbiBDViBpcyBpbW1lZGlhdGVseSBmb3J0aGNvbWluZyB3aXRoIHRob3NlIHVuYmlhc2VkIGVzdGltYXRlcy4NCg0KSSdtIGdvaW5nIHRvIHRha2Ugb25lIG1vcmUgcXVpY2sgbG9vayBiZWZvcmUgd2UgbW92ZSBvbiB0byBhIGRpZmZlcmVudCBtZXRob2QuIEZyb20gdGhpcyBwZXJzcGVjdGl2ZSwgSSdtIGdvaW5nIHRvIG1vdmUgb24gdG8gdGhlIGNyb3NzLWZvbGQgdmFsaWRhdGlvbiBpbiB0aGUgbW9kZWwgc2VsZWN0aW9uIHRvIHNlZSBpZiB3ZSBjYW4gZ2V0IGEgYmV0dGVyIGVzdGltYXRlIG9mIG91ciBzY29yaW5nLiBUbyBkbyB0aGF0LCBJIHdpbGwgaW1wb3J0IHRoZSBjcm9zcy12YWxpZGF0aW9uIG1ldGhvZC4gQW5kIHdlJ3JlIGdvaW5nIHRvIHN3aXRjaCB0byByZWd1bGFyIGxvZ2lzdGljIHJlZ3Jlc3Npb24uDQoNCldoYXQgSSd2ZSBkb25lIGhlcmUgaXMgSSd2ZSBpbXBvcnRlZCBhIG1ldGhvZCBjYWxsZWQgY3Jvc3NfdmFsX3Njb3JlLiBUaGlzIHdpbGwgZ2l2ZSB1cyB0aGUgc2NvcmVzIGFjcm9zcyBhIGNyb3NzLXZhbGlkYXRpb24gZm9yIGFueSBtb2RlbC4gU28gdGhpcyBpcyBub3QgdW5pcXVlIHRvIGxvZ2lzdGljIHJlZ3Jlc3Npb24gbGlrZSB0aGUgbG9naXN0aWMgcmVncmVzc2lvbiBDViB3YXMuIFNvIGxldCdzIGdvIGFoZWFkIGFuZCB1c2UgdGhpcyBjcm9zc192YWxfc2NvcmUgd2l0aCBvdXIgbG9nIHJlZyBtb2RlbCwgd2hpY2ggaXMgYSBzaW1wbGUgbG9naXN0aWMgbW9kZWwuDQoNClNvIHdoYXQgd2UnbGwgbmVlZCB0byBkbyBpcyB3ZSdsbCBuZWVkIHRvIHVuZGVyc3RhbmQgaG93IHRvIGRvIHRoZSBjcm9zc192YWxfc2NvcmUuIEZvciB0aGF0LCBsZXQncyB0YWtlIGEgcXVpY2sgbG9vayBhdCBTSyBMZWFybi4gV2Ugc2VlIGhlcmUgY3Jvc3NfdmFsX3Njb3JlIHRha2VzIGFuIGVzdGltYXRvci4gVGhhdCdzIGdvaW5nIHRvIGJlIG91ciBsb2cgcm9nIG1vZGVsLCBvdXIgeCBhbmQgeSBkYXRhLg0KDQpJdCdzIGdvaW5nIHRvIHRha2Ugb3VyIHNjb3JpbmcuIFNvIHRoYXQgd2lsbCBiZSB3aGF0IG1ldHJpYyB3ZSB3YW50IHRvIHVzZS4gQW5kIHRoZW4gaXQgd2lsbCB1c2UgYSBkZWZhdWx0IGZpdmUtZm9sZCBjcm9zcy12YWxpZGF0aW9uLiBOb3cgeW91J2xsIG5vdGljZSBoZXJlIGl0IHNheXMgbm9uZS4NCg0KSG93ZXZlciwgaWYgd2UgcmVhZCBpbiB0aGUgZGV0YWlscywgbm9uZSB1c2VzIHRoZSBkZWZhdWx0IGZpdmUtZm9sZCBjcm9zcy12YWxpZGF0aW9uLiBUaGlzIGlzIHByb2JhYmx5IHRoZSBvcmlnaW4gb2Ygd2h5IEkgbXlzZWxmIHRlbmQgdG8gdXNlIGZpdmUtZm9sZCBjcm9zcy12YWxpZGF0aW9uIHNvIHRoYXQgd2Uga25vdyBpZiB3ZSBjYWxsIHRoaXMgd2l0aCB0aGUgZGVmYXVsdCBwYXJhbWV0ZXJzLCB3ZSdyZSBnZXR0aW5nIGEgZml2ZS1mb2xkIGNyb3NzIHZhbGlkYXRpb24uIFNvIGxldCdzIGdvIGJhY2sgYW5kIGRvIHRoYXQuIE5vdyB3ZSBuZWVkIG91ciBlc3RpbWF0b3IuDQoNCldlIG5lZWQgb3VyIHggZGF0YSBhbmQgb3VyIHkgZGF0YS4gV2UgcmVtZW1iZXIgdGhhdCB3ZSBkb24ndCBuZWVkIHRvIHNwZWNpZnkgYSBDViBiZWNhdXNlIHRoZSBkZWZhdWx0IGlzIDUuIExldCdzIGFkZCBvdXIgYWNjdXJhY3kgaW4uIFRoaXMgbGluZSBoYXMgZ290dGVuIGEgbGl0dGxlIGxvbmcsIGJ1dCB5b3UgY2FuIHNlZSBJJ3ZlIGdvdCBteSBjcm9zcy12YWxpZGF0aW9uIHNjb3JlLg0KDQpJIHB1dCBpbiB3aGF0IG1vZGVsIEknbSBnb2luZyB0byB1c2UuIEkgaGF2ZSBteSB4IGRhdGEuIEkgaGF2ZSBteSB5IG9yIHRhcmdldCBkYXRhLiBBbmQgSSBoYXZlIHRoZSBzY29yaW5nIEkgd2FudCB0byB1c2UuDQoNCkxldCdzIGdvIGFoZWFkIGFuZCBydW4gaXQuIEFuZCB5b3UgY2FuIHNlZSBoZXJlIHdlJ3ZlIGdvdCBhIHNsaWdodGx5IGxvd2VyIHZhbHVlLiBUaGlzIGlzIGltcG9ydGFudC4gSXQncyBub3QgYmFkLg0KDQpXZSBkaWRuJ3QgZG8gYW55dGhpbmcgaW5jb3JyZWN0bHkuIFdoYXQgdGhlc2UgcmVwcmVzZW50IGFyZSB0aGUgdW5iaWFzZWQgZXN0aW1hdGVzLiBCZWZvcmUgb3VyIDk3JSB3YXMgYSBiaWFzZWQgZXN0aW1hdGUuIFdlIHVzZWQgdGhlIGVudGlyZSBkYXRhIHRvIGZpdCB0aGF0IHBhcnRpY3VsYXIgbW9kZWwuDQoNCkhvd2V2ZXIsIHdoZW4gd2UgcmFuIHRoZSBkYXRhIHRocm91Z2gsIHRoYXQgZGF0YSBoYWQgYmVlbiB1c2VkIHRvIGJ1aWxkIHRoZSBtb2RlbC4gSW4gb3RoZXIgd29yZHMsIHRoZSBtb2RlbCBoYWQgc2VlbiB0aGUgZGF0YSBhaGVhZCBvZiB0aW1lLCByZXN1bHRpbmcgaW4gYmlhc2VkIHJlc3VsdHMuIEhlcmUsIG9mIGNvdXJzZSwgd2Ugc3BsaXQgb3VyIGRhdGEuIEFuZCBzbyB3aGVuIGRhdGEgd2FzIHNlbnQgdGhyb3VnaCBmb3Igc2NvcmluZywgdGhhdCBkYXRhIHdhcyBub3QgdXNlZCB0byBidWlsZCB0aGUgbW9kZWwuDQoNCkFuZCBvZiBjb3Vyc2UsIHRoZSByZWFzb24gd2UgaGF2ZSBmaXZlIHNjb3JlcyB3aXRoIGZpdmUgZGlmZmVyZW50IHZhbHVlcyBpcyB3ZSBzcGxpdCB0aGUgZGF0YSBpbnRvIGZpdmUgcGFydHMuIEFuZCBlYWNoIHRpbWUgdGhhdCBwYXJ0IG9mIHRoZSBkYXRhIHdhcyBoZWxkIG91dCBhcyBhIHRlc3Qgc2V0IGFuZCB0aGUgb3RoZXIgcGFydHMgd2VyZSB1c2VkIHRvIHRyYWluIHRoZSBtb2RlbCBhbmQgZml0IGl0LiBUaGUgdGVzdCBzZXQgd2FzIHRoZW4gcnVuIHRocm91Z2ggd2l0aG91dCB0aGUgbW9kZWwgaGF2aW5nIHNlZW4gaXQgYmVmb3JlIGFuZCB0aHVzIHByb2R1Y2VzIGFuIHVuYmlhc2VkIHByZWRpY3Rpb24uIFNvIHdlIHNlZSB0aGF0IHdoaWxlIHRoZXNlIHByZWRpY3Rpb25zIGFyZSBhIGxvd2VyIHNjb3JlLCB0aGV5J3JlIGFjdHVhbGx5IGEgbW9yZSBhY2N1cmF0ZS0tDQoNCndpdGggbm8gcmVsYXRpb24gdG8gb3VyIHNjb3JpbmcgdXNpbmcgYWNjdXJhY3ksIHRoZXkncmUgYSBtb3JlIGFjY3VyYXRlIHJlcHJlc2VudGF0aW9uIG9mIGhvdyBvdXIgbW9kZWwgd2lsbCBwZXJmb3JtIG9uIHVua25vd24gZGF0YS4gVGhpcyBpcyBhIGdvb2QgZXhhbXBsZSBvZiBob3cgbG9naXN0aWMgcmVncmVzc2lvbiBhbmQgY3Jvc3MtdmFsaWRhdGlvbiBjYW4gYmUgdXNlZCB0b2dldGhlciB0byBnZXQgdGhhdCBlc3RpbWF0ZSBvZiBwZXJmb3JtYW5jZSBvbiBhbiB1bmtub3duIGRhdGEgc2V0LiBBdCB0aGlzIHBvaW50LCB3ZSdyZSBnb2luZyB0byBnbyBhaGVhZCBhbmQgY29uY2x1ZGUgb3VyIGRlbW8gb24gbG9naXN0aWMgcmVncmVzc2lvbi4NCg0KDQo5LiBDYXNlIFN0dWR5IDIgSW50cm9kdWN0aW9uDQpXZWxjb21lIHRvIG91ciBjYXNlIHN0dWR5IG9uIGRpYWJldGVzLiBUaGlzIHdlZWsncyBjYXNlIHN0dWR5IGlzIG9uZSB0aGF0IGhhcyBzb21lIHBvc3NpYmx5IGNvbnRyb3ZlcnNpYWwgZGF0YS4gSSB3YW50IHlvdSB0byBjYXJlZnVsbHkgZm9sbG93IHRoZSBkaXNjdXNzaW9uIGFuZCBkZXZlbG9wIHlvdXIgb3duIHRob3VnaHRzIGFib3V0IHRoZSBtYXRlcmlhbCBiZWluZyBkaXNjdXNzZWQuIFNvbWUgcGVvcGxlIG1heSB3YW50IHRvIGF2b2lkIHRoaXMgdHlwZSBvZiBjb252ZXJzYXRpb24uDQoNCkhvd2V2ZXIsIGFzIGEgZGF0YSBzY2llbnRpc3QsIHdlIGFyZSBvZnRlbiBjb25mcm9udGVkIHdpdGggdGhpbmdzIHRoYXQgaGF2ZSBldGhpY2FsIGNvbmNlcm5zLiBTbyBhcyB5b3Ugd2F0Y2ggdGhpcyB2aWRlbywgY29udGludWUgdG8gbW9uaXRvciBob3cgb3VyIHByb3RhZ29uaXN0IGFza3MgYWJvdXQgdGhlIHByb2JsZW0uIEFuZCB0aGVuIGFsc28gd2F0Y2ggaG93IHRoZXkgcmFpc2UgdGhlaXIgY29uY2VybnMuIFNlZSBpZiB5b3UgaGF2ZSB0aGUgc2FtZSBvciBkaWZmZXJlbnQgY29uY2VybnMuDQoNCkZlZWwgZnJlZSB0byBzdWJtaXQgdGhvc2UgYXMgcGFydCBvZiB5b3VyIHBhcnRpY2lwYXRpb24uIERvbid0IGZvcmdldCwgaW1wb3J0YW50IGRldGFpbHMgYWJvdXQgdGhlIHByb2JsZW0gYXJlIGluY2x1ZGVkIGluIHRoZSB2aWRlby4gT25jZSBhZ2Fpbiwgd2UgYXJlIHNpbXVsYXRpbmcgYSByZWFsIHdvcmxkIHByb2JsZW0gYW5kIGEgcHJlc2VudGF0aW9uIGZyb20gYSBkYXRhIHNjaWVuY2UgcHJvZmVzc2lvbmFsIHRvIGEsIHRoaXMgdGltZSwgc2xpZ2h0bHkgbW9yZSBzZWFzb25lZCBkYXRhIHNjaWVudGlzdC4gQWdhaW4sIHdlIHdpbGwgcGF1c2UgdG8gaGF2ZSB5b3UgdGFrZSBhIGxvb2sgYXQgdGhlIGRhdGEuDQoNCkFuZCB5b3UgY2FuIHN1Ym1pdCBmdXJ0aGVyIHF1ZXN0aW9ucyB0b3dhcmRzIHRoZSBlbmQuIFRob3NlIHF1ZXN0aW9ucyBjYW4gdGhlbiBiZSB1cGxvYWRlZCBhcyBwYXJ0IG9mIHlvdXIgcGFydGljaXBhdGlvbiBncmFkZS4NCg0KMTAuIENhc2UgU3R1ZHkgMiwgUGFydCBJDQoNCkFsbCByaWdodC4gU28gdGhpcyB3ZWVrIHdlIGhhdmUgYSBwcm9ibGVtIGNvbWluZyB0byB1cyBmcm9tIHRoZSBtZWRpY2FsIGNvbW11bml0eS4gV2UncmUgbG9va2luZyBzcGVjaWZpY2FsbHkgYXQgYSBkaWFiZXRlcyBzdHVkeS4gQW5kIHRoZSBwcm9ibGVtIGlzIGhvc3BpdGFsIHJlYWRtaXNzaW9uLg0KDQpOb3cgd2UgZG9uJ3Qgd2FudCBwZW9wbGUgaW4gaG9zcGl0YWxzLiBXZSB3YW50IHRoZW0gdG8gYmUgd2VsbC4gQW5kIHdlIGNlcnRhaW5seSBkb24ndCB3YW50IHRoZW0gdG8gYmUgcmVhZG1pdHRlZC4gVGhpcyBjb21lcyBhdCBhIGh1Z2UgY29zdCB0byB0aGUgcGF0aWVudCBpbiB0ZXJtcyBvZiBiaWxscywgbG9zdCB3YWdlcywgc3RyYWluIG9uIHRoZWlyIGZhbWlseSBhbmQgd2hhdG5vdC4NCg0KU28gb3VyIGdvYWwgaXMgbm8gcmVhZG1pc3Npb24uIFNvIGZvciB0aGlzIHN0dWR5LCB3aGF0IHdlJ3JlIHRyeWluZyB0byBkbyBpcyB3ZSB3YW50IHRvIHRyeSB0byBwcmVkaWN0IHJlYWRtaXNzaW9uIG9mIHRoZSBwYXRpZW50IHdpdGhpbiAzMCBkYXlzIG9mIGluaXRpYWwgaG9zcGl0YWxpemF0aW9uLiBTbyB0YWtlIGEgbG9vayBhdCB0aGF0IGRhdGEgYW5kIHdlJ2xsIGNvbWUgYmFjayBhbmQgdGFrZSBhbnkgcXVlc3Rpb25zIHlvdSBoYXZlLg0KDQoxMS4gQ2FzZSBTdHVkeSAyLCBQYXJ0IElJDQogDQogU28geW91J3ZlIGhhZCBhIGNoYW5jZSB0byBsb29rIGF0IHRoZSBkYXRhLiBEbyB5b3UgaGF2ZSBhbnkgcXVlc3Rpb25zIGFib3V0IGl0PyBJIGFjdHVhbGx5IGRvIGhhdmUgYSBjb3VwbGUgb2YgcXVlc3Rpb25zIGFib3V0IHRoaXMgcGFydGljdWxhciBhc3NpZ25tZW50LiBBbmQgdGhlIGZpcnN0IG9uZSBpcywgd2VsbCwgcHJldHR5IGltcG9ydGFudCB0byBtZS4NCg0KSXQncyByZWdhcmRpbmcgdGhlIHJhY2UgY2F0ZWdvcnkuIEFuZCBJIGRvbid0IHVuZGVyc3RhbmQgaXRzIHNpZ25pZmljYW5jZS4gSXQgc2VlbXMgYWN0dWFsbHkga2luZCBvZiBsaWtlIHNvcnRpbmcgcGF0aWVudHMgd2l0aCB0aGlzIGNyaXRlcmlhIGNvdWxkIGJlIHNlZW4gYXMgcmFjaWFsbHkgYmlhc2VkIG9yIHNvbWV0aGluZy4gSSBtZWFuLCBzaW1wbHkgdGFsa2luZyBhYm91dCBpZGVudGlmeWluZyB3aG8gd2FzIGdldHRpbmcgcmVhZG1pdHRlZCB0byB0aGUgaG9zcGl0YWwuDQoNClNvIEkgcmVhbGx5IGRvbid0IHVuZGVyc3RhbmQgd2h5IHJhY2UgbWF0dGVycy4gT0suIEFic29sdXRlbHkuIEFuZCB0aGFuayB5b3UgZm9yIGJyaW5naW5nIHRoYXQgdXAgYmVjYXVzZSBJIGFic29sdXRlbHkgYWdyZWUgd2l0aCB5b3UuDQoNCk5vcm1hbGx5LCB0aGlzIGlzIG5vdCBzb21ldGhpbmcgdGhhdCB3ZSB3b3VsZCB0YWtlIGludG8gY29uc2lkZXJhdGlvbi4gU29ydGluZyBieSByYWNlIGNhbiBicmluZyBpbiBhIGxvdCBvZiBldGhpY2FsIGNvbnNpZGVyYXRpb25zLiBJbiB0aGlzIGNhc2UsIHdlJ3JlIHRhbGtpbmcgYWJvdXQgdGhlIG1lZGljYWwgY29tbXVuaXR5LiBXZSdyZSB0YWxraW5nIGFib3V0IHBhdGllbnRzLg0KDQpBbmQgd2UgZG8ga25vdyB0aGF0IGRpYWJldGVzIGFmZmVjdHMgZGlmZmVyZW50IGRlbW9ncmFwaGljcyBkaWZmZXJlbnRseS4gU28gcmFjZSBhY3R1YWxseSBjb3VsZCB2ZXJ5IHdlbGwgYmUgYSBmYWN0b3IgaW4gdGhpcy4gTm93LCB0aGF0IGJlaW5nIHNhaWQsIEkgd2lsbCBsZWF2ZSBpdCB0byB5b3UuIEFzIGxvbmcgYXMgd2UgY2FuIGNoYXJ0IHRoZSB0cmVuZHMgYWNjdXJhdGVseSwgSSdtIG5vdCBhcyBjb25jZXJuZWQgYWJvdXQgaG93IHdlIGdldCB0aGVyZS4NCg0KT0suIEdvdCBpdC4gVGhhbmsgeW91LiBTbyBteSBuZXh0IHF1ZXN0aW9uIGlzIGFsbCBhYm91dCBhbGwgb2YgdGhlc2UgcXVlc3Rpb24gbWFya3MgdGhhdCBhcmUgaW4gdGhlIGRhdGEgc2V0Lg0KDQpXaGF0J3MgZ29pbmcgb24gd2l0aCB0aGF0PyBZZWFoLiBUaGUgc3R1ZHkgdG9vayBwbGFjZSBvdmVyIDEwIHllYXJzLiBUaGVyZSdzIHNvbWV0aGluZyBsaWtlIDEzMCBob3NwaXRhbHMgdGhhdCB0aGV5IHdlcmUgcHVsbGluZyBkYXRhIGZyb20uDQoNClRoZXJlIGFyZSBhIGxvdCBvZiBwZW9wbGUgZW50ZXJpbmcgZGF0YSBpbnRvIHRoaXMgc3R1ZHkuIEFuZCBzbyB0aGVyZSBhcmUgaG9sZXMuIFNvIHdlJ3JlIGp1c3QgZ29pbmcgdG8gaGF2ZSB0byBtYWtlIHRoZSBiZXN0IHJlY29tbWVuZGF0aW9uIHdlIGNhbiBiYXNlZCBvbiB0aGUgZGF0YSB0aGF0IHdlIGhhdmUuIE9LLg0KDQpVbmRlcnN0b29kLiBJJ2xsIGdldCB3b3JraW5nIHJpZ2h0IGF3YXkuIFRoYW5rIHlvdS4NCg0KWW91IHdpbGwgc3VibWl0IHlvdXIgYXNzaWdubWVudCB0byB0aGlzIHBhZ2UuDQoNClBsZWFzZSByZWZlciB0byB5b3VyIGNvdXJzZSBzeWxsYWJ1cyBmb3IgYWRkaXRpb25hbCBkZXRhaWxzIGFib3V0IHRoaXMgYXNzaWdubWVudC4NCg0KV2hlbiB5b3Ugc2F2ZSB5b3VyIGFzc2lnbm1lbnQsIHRoZSBmaWxlIG5hbWUgc2hvdWxkIGJlICJGaXJzdCBOYW1lX0xhc3QgTmFtZV9Bc3NpZ25tZW50IE5hbWUuIg0KDQpZb3VyIGNhc2Ugc3R1ZHkgaXMgdG8gYnVpbGQgY2xhc3NpZmllcnMgdXNpbmcgbG9naXN0aWMgcmVncmVzc2lvbiB0byBwcmVkaWN0IEFMTCAzIENBVEVHT1JJRVMgb2YgaG9zcGl0YWwgcmUtYWRtaXR0YW5jZS4gVGhlcmUgaXMgbWlzc2luZyBkYXRhIHRoYXQgbXVzdCBiZSBpbXB1dGVkLg0KDQpPbmNlIGFnYWluLCBkaXNjdXNzIHRoZSB0b3AgNSB2YXJpYWJsZSBpbXBvcnRhbmNlIG9mIHlvdXIgYmVzdCBtb2RlbCBhcyBwYXJ0IG9mIHlvdXIgc3VibWlzc2lvDQoNCiMjIyAqKldoYXQgaXMgRGlzY3JldGUgRGF0YT8qKg0KDQoqKkRpc2NyZXRlIGRhdGEqKiByZWZlcnMgdG8gYSB0eXBlIG9mIHF1YW50aXRhdGl2ZSBkYXRhIHRoYXQgY29uc2lzdHMgb2YgZGlzdGluY3QsIHNlcGFyYXRlIHZhbHVlcy4gVGhlc2UgdmFsdWVzIGFyZSBjb3VudGFibGUgYW5kIGNhbm5vdCB0YWtlIG9uIGV2ZXJ5IHBvc3NpYmxlIHZhbHVlIHdpdGhpbiBhIHJhbmdlLiBJbiBvdGhlciB3b3JkcywgZGlzY3JldGUgZGF0YSBpcyBtYWRlIHVwIG9mIHdob2xlIG51bWJlcnMgb3Igc3BlY2lmaWMgY2F0ZWdvcmllcywgYW5kIHRoZXJlIGFyZSBubyBpbnRlcm1lZGlhdGUgdmFsdWVzIGJldHdlZW4gdHdvIGRhdGEgcG9pbnRzLg0KDQotLS0NCg0KIyMjICoqS2V5IENoYXJhY3RlcmlzdGljcyBvZiBEaXNjcmV0ZSBEYXRhKioNCjEuICoqQ291bnRhYmxlKio6IERpc2NyZXRlIGRhdGEgcmVwcmVzZW50cyBpdGVtcyB0aGF0IGNhbiBiZSBjb3VudGVkIChlLmcuLCB0aGUgbnVtYmVyIG9mIHN0dWRlbnRzIGluIGEgY2xhc3MpLg0KMi4gKipGaW5pdGUgb3IgSW5maW5pdGUqKjogSXQgY2FuIGhhdmUgYSBmaW5pdGUgc2V0IG9mIHZhbHVlcyAoZS5nLiwgZGljZSByb2xsczogMSwgMiwgMywgNCwgNSwgNikgb3IgYW4gaW5maW5pdGUgc2V0IChlLmcuLCB0aGUgbnVtYmVyIG9mIHRpbWVzIGEgY29pbiBpcyBmbGlwcGVkIHVudGlsIGhlYWRzIGFwcGVhcnMpLg0KMy4gKipObyBGcmFjdGlvbnMgb3IgRGVjaW1hbHMqKjogRGlzY3JldGUgZGF0YSBjYW5ub3QgdGFrZSBmcmFjdGlvbmFsIG9yIGRlY2ltYWwgdmFsdWVzIChlLmcuLCB5b3UgY2FuJ3QgaGF2ZSAyLjUgc3R1ZGVudHMpLg0KNC4gKipDYXRlZ29yaWNhbCBvciBOdW1lcmljYWwqKjogV2hpbGUgZGlzY3JldGUgZGF0YSBpcyBvZnRlbiBudW1lcmljYWwsIGl0IGNhbiBhbHNvIHJlcHJlc2VudCBjYXRlZ29yaWVzIChlLmcuLCB0eXBlcyBvZiBjYXJzOiBzZWRhbiwgU1VWLCB0cnVjaykuDQoNCi0tLQ0KDQojIyMgKipFeGFtcGxlcyBvZiBEaXNjcmV0ZSBEYXRhKioNCjEuICoqTnVtZXJpY2FsIEV4YW1wbGVzKio6DQogICAtIE51bWJlciBvZiBjaGlsZHJlbiBpbiBhIGZhbWlseSAoZS5nLiwgMCwgMSwgMiwgMykuDQogICAtIE51bWJlciBvZiBjYXJzIGluIGEgcGFya2luZyBsb3QuDQogICAtIE51bWJlciBvZiBnb2FscyBzY29yZWQgaW4gYSBzb2NjZXIgbWF0Y2guDQoNCjIuICoqQ2F0ZWdvcmljYWwgRXhhbXBsZXMqKjoNCiAgIC0gU2hvZSBzaXplcyAoZS5nLiwgNywgOCwgOSwgMTApLg0KICAgLSBUeXBlcyBvZiBmcnVpdHMgaW4gYSBiYXNrZXQgKGUuZy4sIGFwcGxlcywgb3JhbmdlcywgYmFuYW5hcykuDQoNCi0tLQ0KDQojIyMgKipEaXNjcmV0ZSB2cy4gQ29udGludW91cyBEYXRhKioNCnwgKipGZWF0dXJlKiogICAgICAgICAgICB8ICoqRGlzY3JldGUgRGF0YSoqICAgICAgICAgICAgICAgICAgICAgICAgICB8ICoqQ29udGludW91cyBEYXRhKiogICAgICAgICAgICAgICAgICAgICAgIHwNCnwtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tfC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tfC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS18DQp8ICoqRGVmaW5pdGlvbioqICAgICAgICAgIHwgQ291bnRhYmxlLCBkaXN0aW5jdCB2YWx1ZXMgICAgICAgICAgICAgICAgfCBNZWFzdXJhYmxlLCBpbmZpbml0ZSB2YWx1ZXMgd2l0aGluIGEgcmFuZ2V8DQp8ICoqRXhhbXBsZXMqKiAgICAgICAgICAgIHwgTnVtYmVyIG9mIHN0dWRlbnRzLCBkaWNlIHJvbGxzICAgICAgICAgICAgfCBIZWlnaHQsIHdlaWdodCwgdGVtcGVyYXR1cmUgICAgICAgICAgICAgICB8DQp8ICoqVmFsdWVzKiogICAgICAgICAgICAgIHwgV2hvbGUgbnVtYmVycyBvciBjYXRlZ29yaWVzICAgICAgICAgICAgICAgfCBDYW4gaW5jbHVkZSBmcmFjdGlvbnMgYW5kIGRlY2ltYWxzICAgICAgICB8DQp8ICoqR3JhcGggUmVwcmVzZW50YXRpb24qKnwgQmFyIGNoYXJ0cywgcGllIGNoYXJ0cyAgICAgICAgICAgICAgICAgICAgfCBIaXN0b2dyYW1zLCBsaW5lIGdyYXBocyAgICAgICAgICAgICAgICAgICB8DQoNCi0tLQ0KDQojIyMgKipIb3cgdG8gQW5hbHl6ZSBEaXNjcmV0ZSBEYXRhKioNCjEuICoqVmlzdWFsaXphdGlvbioqOg0KICAgLSBVc2UgKipiYXIgY2hhcnRzKiogb3IgKipwaWUgY2hhcnRzKiogdG8gcmVwcmVzZW50IGRpc2NyZXRlIGRhdGEgdmlzdWFsbHkuDQogICAtIEV4YW1wbGU6IEEgYmFyIGNoYXJ0IHNob3dpbmcgdGhlIG51bWJlciBvZiBzdHVkZW50cyBpbiBlYWNoIGdyYWRlLg0KDQoyLiAqKlN0YXRpc3RpY2FsIE1lYXN1cmVzKio6DQogICAtIENhbGN1bGF0ZSBtZWFzdXJlcyBsaWtlICoqbWVhbioqLCAqKm1lZGlhbioqLCAqKm1vZGUqKiwgYW5kICoqcmFuZ2UqKi4NCiAgIC0gRXhhbXBsZTogVGhlIGF2ZXJhZ2UgbnVtYmVyIG9mIGNhcnMgc29sZCBieSBhIGRlYWxlcnNoaXAgcGVyIGRheS4NCg0KMy4gKipQcm9iYWJpbGl0eSoqOg0KICAgLSBEaXNjcmV0ZSBkYXRhIGlzIG9mdGVuIHVzZWQgaW4gcHJvYmFiaWxpdHkgZGlzdHJpYnV0aW9ucywgc3VjaCBhcyB0aGUgKipiaW5vbWlhbCBkaXN0cmlidXRpb24qKiBvciAqKlBvaXNzb24gZGlzdHJpYnV0aW9uKiouDQoNCi0tLQ0KDQojIyMgKipBcHBsaWNhdGlvbnMgb2YgRGlzY3JldGUgRGF0YSoqDQotICoqQnVzaW5lc3MqKjogVHJhY2tpbmcgdGhlIG51bWJlciBvZiBwcm9kdWN0cyBzb2xkIG9yIGN1c3RvbWVyIGNvbXBsYWludHMuDQotICoqRWR1Y2F0aW9uKio6IENvdW50aW5nIHRoZSBudW1iZXIgb2Ygc3R1ZGVudHMgaW4gZGlmZmVyZW50IGNsYXNzZXMuDQotICoqSGVhbHRoY2FyZSoqOiBSZWNvcmRpbmcgdGhlIG51bWJlciBvZiBwYXRpZW50cyB2aXNpdGluZyBhIGNsaW5pYyBkYWlseS4NCi0gKipTcG9ydHMqKjogQ291bnRpbmcgZ29hbHMsIHBvaW50cywgb3Igd2lucyBpbiBhIGdhbWUuDQoNCi0tLQ0KDQojIyMgKipDb25jbHVzaW9uKioNCkRpc2NyZXRlIGRhdGEgaXMgYSBmdW5kYW1lbnRhbCBjb25jZXB0IGluIHN0YXRpc3RpY3MgYW5kIGRhdGEgYW5hbHlzaXMuIEl0IHJlcHJlc2VudHMgY291bnRhYmxlLCBkaXN0aW5jdCB2YWx1ZXMgYW5kIGlzIG9mdGVuIHVzZWQgaW4gc2NlbmFyaW9zIHdoZXJlIHdob2xlIG51bWJlcnMgb3Igc3BlY2lmaWMgY2F0ZWdvcmllcyBhcmUgaW52b2x2ZWQuIFVuZGVyc3RhbmRpbmcgZGlzY3JldGUgZGF0YSBpcyBlc3NlbnRpYWwgZm9yIGFuYWx5emluZyBhbmQgaW50ZXJwcmV0aW5nIHJlYWwtd29ybGQgcGhlbm9tZW5hIGVmZmVjdGl2ZWx5Lg0KDQojIyMgU3VtbWFyeTogVHJhbnNpdGlvbmluZyBmcm9tIExpbmVhciBSZWdyZXNzaW9uIHRvIExvZ2lzdGljIFJlZ3Jlc3Npb24NCg0KVGhpcyBleHBsYW5hdGlvbiBmb2N1c2VzIG9uIHRoZSB0cmFuc2l0aW9uIGZyb20gKipsaW5lYXIgcmVncmVzc2lvbioqIHRvICoqbG9naXN0aWMgcmVncmVzc2lvbioqLCBwYXJ0aWN1bGFybHkgd2hlbiBkZWFsaW5nIHdpdGggKipjYXRlZ29yaWNhbCBkYXRhKiogYW5kIGJpbmFyeSBjbGFzc2lmaWNhdGlvbiB0YXNrcy4NCg0KMS4gKipQcm9ibGVtIHdpdGggTGluZWFyIFJlZ3Jlc3Npb24gZm9yIENhdGVnb3JpY2FsIFRhcmdldHMqKjoNCiAgIC0gTGluZWFyIHJlZ3Jlc3Npb24gcHJlZGljdHMgY29udGludW91cyBvdXRwdXRzLCB3aGljaCBhcmUgbm90IHN1aXRhYmxlIGZvciBjYXRlZ29yaWNhbCB0YXJnZXRzIChlLmcuLCBwcmVkaWN0aW5nIDAgb3IgMSBmb3IgYmluYXJ5IGNsYXNzaWZpY2F0aW9uKS4NCiAgIC0gRXZlbiBpZiB0aGUgb3V0cHV0IGlzIGNvbnN0cmFpbmVkIGJldHdlZW4gMCBhbmQgMSwgbGluZWFyIHJlZ3Jlc3Npb24gY2FuIHN0aWxsIHByb2R1Y2UgdmFsdWVzIG91dHNpZGUgdGhpcyByYW5nZSwgbWFraW5nIGl0IHVucmVsaWFibGUgZm9yIGNsYXNzaWZpY2F0aW9uIHRhc2tzLg0KDQoyLiAqKkRpc2NyZXRlIHZzLiBDb250aW51b3VzIE91dHB1dHMqKjoNCiAgIC0gQ29udGludW91cyBvdXRwdXRzIHZhcnkgc21vb3RobHkgd2l0aCBpbnB1dCB2YWx1ZXMgKGUuZy4sIGxpbmVhciByZWdyZXNzaW9uKS4NCiAgIC0gRGlzY3JldGUgb3V0cHV0cywgbGlrZSBiaW5hcnkgdGFyZ2V0cyAoMCBvciAxKSwgb25seSB0YWtlIHNwZWNpZmljIHZhbHVlcyBhbmQgYXJlIGJldHRlciBzdWl0ZWQgZm9yIGNsYXNzaWZpY2F0aW9uIHRhc2tzLg0KDQozLiAqKkJpbmFyeSBDbGFzc2lmaWNhdGlvbioqOg0KICAgLSBUaGUgdGFyZ2V0IHZhcmlhYmxlIGlzIGJpbmFyeSAoZS5nLiwgdHJ1ZS9mYWxzZSwgMC8xKS4NCiAgIC0gT25lLWhvdCBlbmNvZGluZyBpcyB1c2VkIHRvIHRyYW5zZm9ybSBjYXRlZ29yaWNhbCBkYXRhIGludG8gbnVtZXJpYyBkYXRhLCBidXQgbGluZWFyIHJlZ3Jlc3Npb24gY2Fubm90IGRpcmVjdGx5IGhhbmRsZSB0aGlzIGZvciBjbGFzc2lmaWNhdGlvbi4NCg0KNC4gKipJbnRyb2R1Y2luZyBMb2dpc3RpYyBSZWdyZXNzaW9uKio6DQogICAtIExvZ2lzdGljIHJlZ3Jlc3Npb24gYXBwbGllcyBhICoqdmFyaWFibGUgdHJhbnNmb3JtYXRpb24qKiB0byBjb252ZXJ0IHRoZSBjb250aW51b3VzIG91dHB1dCBvZiBsaW5lYXIgcmVncmVzc2lvbiBpbnRvIGEgZGlzY3JldGUgcHJvYmFiaWxpdHkuDQogICAtIFRoaXMgdHJhbnNmb3JtYXRpb24gaXMgYWNoaWV2ZWQgdXNpbmcgdGhlICoqc2lnbW9pZCBmdW5jdGlvbioqIChkZW5vdGVkIGFzIM+DKSwgd2hpY2ggbWFwcyBhbnkgaW5wdXQgdG8gYSB2YWx1ZSBiZXR3ZWVuIDAgYW5kIDEuDQoNCjUuICoqU2lnbW9pZCBGdW5jdGlvbioqOg0KICAgLSBUaGUgc2lnbW9pZCBmdW5jdGlvbiB0YWtlcyB0aGUgbGluZWFyIHJlZ3Jlc3Npb24gZXF1YXRpb24gKGB5ID0gbXggKyBiYCkgYXMgaW5wdXQgYW5kIG91dHB1dHMgYSBwcm9iYWJpbGl0eSB2YWx1ZSBiZXR3ZWVuIDAgYW5kIDEuDQogICAtIFRoaXMgZW5zdXJlcyB0aGUgb3V0cHV0IGlzIHN1aXRhYmxlIGZvciBiaW5hcnkgY2xhc3NpZmljYXRpb24gdGFza3MuDQoNCjYuICoqQ29tcGFjdCBOb3RhdGlvbioqOg0KICAgLSBMb2dpc3RpYyByZWdyZXNzaW9uIGJ1aWxkcyBvbiB0aGUgZmFtaWxpYXIgbGluZWFyIHJlZ3Jlc3Npb24gZXF1YXRpb24gKGB5ID0gbTF4MSArIG0yeDIgKyAuLi4gKyBiYCksIGJ1dCBhcHBsaWVzIHRoZSBzaWdtb2lkIGZ1bmN0aW9uIHRvIHRoZSByZXN1bHQuDQogICAtIFRoZSBzdW1tYXRpb24gc2lnbiBpcyBvZnRlbiBpbXBsaWVkIGZvciBicmV2aXR5LCBhcyB0aGUgc3RydWN0dXJlIG9mIHRoZSBlcXVhdGlvbiByZW1haW5zIHNpbWlsYXIgdG8gbGluZWFyIHJlZ3Jlc3Npb24uDQoNCiMjIyBLZXkgVGFrZWF3YXk6DQpMb2dpc3RpYyByZWdyZXNzaW9uIGV4dGVuZHMgbGluZWFyIHJlZ3Jlc3Npb24gYnkgaW50cm9kdWNpbmcgYSAqKnNpZ21vaWQgZnVuY3Rpb24qKiB0byBtb2RlbCAqKmJpbmFyeSBjYXRlZ29yaWNhbCBvdXRwdXRzKiouIFRoaXMgdHJhbnNmb3JtYXRpb24gZW5zdXJlcyB0aGUgb3V0cHV0IGlzIGNvbnN0cmFpbmVkIGJldHdlZW4gMCBhbmQgMSwgbWFraW5nIGl0IHN1aXRhYmxlIGZvciBjbGFzc2lmaWNhdGlvbiB0YXNrcyB3aGVyZSB0aGUgdGFyZ2V0IGlzIGRpc2NyZXRlLg0KDQojIyMgU3VtbWFyeTogVHJhbnNpdGlvbmluZyBmcm9tIExpbmVhciBSZWdyZXNzaW9uIHRvIExvZ2lzdGljIFJlZ3Jlc3Npb24NCg0KVGhpcyBleHBsYW5hdGlvbiBmb2N1c2VzIG9uIHRoZSB0cmFuc2l0aW9uIGZyb20gKipsaW5lYXIgcmVncmVzc2lvbioqIHRvICoqbG9naXN0aWMgcmVncmVzc2lvbioqLCBwYXJ0aWN1bGFybHkgd2hlbiBkZWFsaW5nIHdpdGggKipjYXRlZ29yaWNhbCBkYXRhKiogYW5kIGJpbmFyeSBjbGFzc2lmaWNhdGlvbiB0YXNrcy4NCg0KMS4gKipQcm9ibGVtIHdpdGggTGluZWFyIFJlZ3Jlc3Npb24gZm9yIENhdGVnb3JpY2FsIFRhcmdldHMqKjoNCiAgIC0gTGluZWFyIHJlZ3Jlc3Npb24gcHJlZGljdHMgY29udGludW91cyBvdXRwdXRzLCB3aGljaCBhcmUgbm90IHN1aXRhYmxlIGZvciBjYXRlZ29yaWNhbCB0YXJnZXRzIChlLmcuLCBwcmVkaWN0aW5nIDAgb3IgMSBmb3IgYmluYXJ5IGNsYXNzaWZpY2F0aW9uKS4NCiAgIC0gRXZlbiBpZiB0aGUgb3V0cHV0IGlzIGNvbnN0cmFpbmVkIGJldHdlZW4gMCBhbmQgMSwgbGluZWFyIHJlZ3Jlc3Npb24gY2FuIHN0aWxsIHByb2R1Y2UgdmFsdWVzIG91dHNpZGUgdGhpcyByYW5nZSwgbWFraW5nIGl0IHVucmVsaWFibGUgZm9yIGNsYXNzaWZpY2F0aW9uIHRhc2tzLg0KDQoyLiAqKkRpc2NyZXRlIHZzLiBDb250aW51b3VzIE91dHB1dHMqKjoNCiAgIC0gQ29udGludW91cyBvdXRwdXRzIHZhcnkgc21vb3RobHkgd2l0aCBpbnB1dCB2YWx1ZXMgKGUuZy4sIGxpbmVhciByZWdyZXNzaW9uKS4NCiAgIC0gRGlzY3JldGUgb3V0cHV0cywgbGlrZSBiaW5hcnkgdGFyZ2V0cyAoMCBvciAxKSwgb25seSB0YWtlIHNwZWNpZmljIHZhbHVlcyBhbmQgYXJlIGJldHRlciBzdWl0ZWQgZm9yIGNsYXNzaWZpY2F0aW9uIHRhc2tzLg0KDQozLiAqKkJpbmFyeSBDbGFzc2lmaWNhdGlvbioqOg0KICAgLSBUaGUgdGFyZ2V0IHZhcmlhYmxlIGlzIGJpbmFyeSAoZS5nLiwgdHJ1ZS9mYWxzZSwgMC8xKS4NCiAgIC0gT25lLWhvdCBlbmNvZGluZyBpcyB1c2VkIHRvIHRyYW5zZm9ybSBjYXRlZ29yaWNhbCBkYXRhIGludG8gbnVtZXJpYyBkYXRhLCBidXQgbGluZWFyIHJlZ3Jlc3Npb24gY2Fubm90IGRpcmVjdGx5IGhhbmRsZSB0aGlzIGZvciBjbGFzc2lmaWNhdGlvbi4NCg0KNC4gKipJbnRyb2R1Y2luZyBMb2dpc3RpYyBSZWdyZXNzaW9uKio6DQogICAtIExvZ2lzdGljIHJlZ3Jlc3Npb24gYXBwbGllcyBhICoqdmFyaWFibGUgdHJhbnNmb3JtYXRpb24qKiB0byBjb252ZXJ0IHRoZSBjb250aW51b3VzIG91dHB1dCBvZiBsaW5lYXIgcmVncmVzc2lvbiBpbnRvIGEgZGlzY3JldGUgcHJvYmFiaWxpdHkuDQogICAtIFRoaXMgdHJhbnNmb3JtYXRpb24gaXMgYWNoaWV2ZWQgdXNpbmcgdGhlICoqc2lnbW9pZCBmdW5jdGlvbioqIChkZW5vdGVkIGFzIM+DKSwgd2hpY2ggbWFwcyBhbnkgaW5wdXQgdG8gYSB2YWx1ZSBiZXR3ZWVuIDAgYW5kIDEuDQoNCjUuICoqU2lnbW9pZCBGdW5jdGlvbioqOg0KICAgLSBUaGUgc2lnbW9pZCBmdW5jdGlvbiB0YWtlcyB0aGUgbGluZWFyIHJlZ3Jlc3Npb24gZXF1YXRpb24gKGB5ID0gbXggKyBiYCkgYXMgaW5wdXQgYW5kIG91dHB1dHMgYSBwcm9iYWJpbGl0eSB2YWx1ZSBiZXR3ZWVuIDAgYW5kIDEuDQogICAtIFRoaXMgZW5zdXJlcyB0aGUgb3V0cHV0IGlzIHN1aXRhYmxlIGZvciBiaW5hcnkgY2xhc3NpZmljYXRpb24gdGFza3MuDQoNCjYuICoqQ29tcGFjdCBOb3RhdGlvbioqOg0KICAgLSBMb2dpc3RpYyByZWdyZXNzaW9uIGJ1aWxkcyBvbiB0aGUgZmFtaWxpYXIgbGluZWFyIHJlZ3Jlc3Npb24gZXF1YXRpb24gKGB5ID0gbTF4MSArIG0yeDIgKyAuLi4gKyBiYCksIGJ1dCBhcHBsaWVzIHRoZSBzaWdtb2lkIGZ1bmN0aW9uIHRvIHRoZSByZXN1bHQuDQogICAtIFRoZSBzdW1tYXRpb24gc2lnbiBpcyBvZnRlbiBpbXBsaWVkIGZvciBicmV2aXR5LCBhcyB0aGUgc3RydWN0dXJlIG9mIHRoZSBlcXVhdGlvbiByZW1haW5zIHNpbWlsYXIgdG8gbGluZWFyIHJlZ3Jlc3Npb24uDQoNCiMjIyBLZXkgVGFrZWF3YXk6DQpMb2dpc3RpYyByZWdyZXNzaW9uIGV4dGVuZHMgbGluZWFyIHJlZ3Jlc3Npb24gYnkgaW50cm9kdWNpbmcgYSAqKnNpZ21vaWQgZnVuY3Rpb24qKiB0byBtb2RlbCAqKmJpbmFyeSBjYXRlZ29yaWNhbCBvdXRwdXRzKiouIFRoaXMgdHJhbnNmb3JtYXRpb24gZW5zdXJlcyB0aGUgb3V0cHV0IGlzIGNvbnN0cmFpbmVkIGJldHdlZW4gMCBhbmQgMSwgbWFraW5nIGl0IHN1aXRhYmxlIGZvciBjbGFzc2lmaWNhdGlvbiB0YXNrcyB3aGVyZSB0aGUgdGFyZ2V0IGlzIGRpc2NyZXRlLg0KVGhpcyBzbGlkZSBpbnRyb2R1Y2VzICoqbG9naXN0aWMgcmVncmVzc2lvbioqIGJ5IGJ1aWxkaW5nIG9uIHRoZSBmYW1pbGlhciAqKmxpbmVhciByZWdyZXNzaW9uIGVxdWF0aW9uKiogYW5kIGFwcGx5aW5nIGEgdHJhbnNmb3JtYXRpb24gdG8gbWFrZSBpdCBzdWl0YWJsZSBmb3IgY2xhc3NpZmljYXRpb24gdGFza3MsIHBhcnRpY3VsYXJseSBiaW5hcnkgY2xhc3NpZmljYXRpb24uDQoNCi0tLQ0KDQojIyMgKioxLiBMaW5lYXIgUmVncmVzc2lvbiBFcXVhdGlvbioqDQpUaGUgb3JpZ2luYWwgbGluZWFyIHJlZ3Jlc3Npb24gZXF1YXRpb24gaXM6DQpcWw0KeSA9IG1fMXhfMSArIG1fMnhfMiArIFxkb3RzID0gXHN1bSBtX2l4X2kNClxdDQotICoqRXhwbGFuYXRpb24qKjoNCiAgLSBcKG1faVwpOiBDb2VmZmljaWVudHMgKHNsb3BlcykgZm9yIGVhY2ggZmVhdHVyZSBcKHhfaVwpLg0KICAtIFwoeF9pXCk6IElucHV0IGZlYXR1cmVzIChpbmRlcGVuZGVudCB2YXJpYWJsZXMpLg0KICAtIFwoeVwpOiBPdXRwdXQgKGRlcGVuZGVudCB2YXJpYWJsZSksIHdoaWNoIGlzIGNvbnRpbnVvdXMgaW4gbGluZWFyIHJlZ3Jlc3Npb24uDQoNCi0tLQ0KDQojIyMgKioyLiBQcm9ibGVtIHdpdGggTGluZWFyIFJlZ3Jlc3Npb24gZm9yIENsYXNzaWZpY2F0aW9uKioNCi0gTGluZWFyIHJlZ3Jlc3Npb24gcHJvZHVjZXMgY29udGludW91cyBvdXRwdXRzLCB3aGljaCBhcmUgbm90IGlkZWFsIGZvciBjbGFzc2lmaWNhdGlvbiB0YXNrcyB3aGVyZSB0aGUgdGFyZ2V0IGlzIGNhdGVnb3JpY2FsIChlLmcuLCAwIG9yIDEgZm9yIGJpbmFyeSBjbGFzc2lmaWNhdGlvbikuDQotIFRvIGFkZHJlc3MgdGhpcywgd2UgbmVlZCB0byB0cmFuc2Zvcm0gdGhlIG91dHB1dCBpbnRvIGEgKipwcm9iYWJpbGl0eSoqIHRoYXQgbGllcyBiZXR3ZWVuIDAgYW5kIDEuDQoNCi0tLQ0KDQojIyMgKiozLiBMb2dpc3RpYyBSZWdyZXNzaW9uIEVxdWF0aW9uKioNClRoZSBsb2dpc3RpYyByZWdyZXNzaW9uIGVxdWF0aW9uIGludHJvZHVjZXMgdGhlICoqc2lnbW9pZCBmdW5jdGlvbioqIHRvIHRyYW5zZm9ybSB0aGUgb3V0cHV0Og0KXFsNCnkgPSBcZnJhY3sxfXsxICsgZV57LVxzdW0gbV9peF9pfX0NClxdDQotICoqRXhwbGFuYXRpb24qKjoNCiAgLSBUaGUgc2lnbW9pZCBmdW5jdGlvbiBtYXBzIGFueSByZWFsIG51bWJlciAoZnJvbSBcKC1caW5mdHlcKSB0byBcKCtcaW5mdHlcKSkgdG8gYSB2YWx1ZSBiZXR3ZWVuIDAgYW5kIDEuDQogIC0gVGhpcyBtYWtlcyB0aGUgb3V0cHV0IGludGVycHJldGFibGUgYXMgYSAqKnByb2JhYmlsaXR5KiogZm9yIGJpbmFyeSBjbGFzc2lmaWNhdGlvbi4NCg0KLS0tDQoNCiMjIyAqKjQuIENvbXBhY3QgTm90YXRpb24qKg0KVGhlIGVxdWF0aW9uIGNhbiBiZSB3cml0dGVuIG1vcmUgY29tcGFjdGx5IGFzOg0KXFsNCnkgPSBcc2lnbWEoXHN1bSBtX2l4X2kpDQpcXQ0KLSAqKlwoXHNpZ21hXCkqKjogVGhlIHNpZ21vaWQgZnVuY3Rpb24sIGRlZmluZWQgYXM6DQogIFxbDQogIFxzaWdtYSh6KSA9IFxmcmFjezF9ezEgKyBlXnsten19DQogIFxdDQotIFRoZSBzdW1tYXRpb24gc2lnbiAoXChcc3VtXCkpIGlzIG9mdGVuIGRyb3BwZWQgZm9yIGNsYXJpdHksIGFzIGl0IGlzIGltcGxpZWQgdGhhdCB0aGUgaW5wdXQgaXMgdGhlIHdlaWdodGVkIHN1bSBvZiBmZWF0dXJlcyAoXChtX2l4X2lcKSkuDQoNCi0tLQ0KDQojIyMgKipLZXkgVGFrZWF3YXlzKioNCi0gKipMaW5lYXIgcmVncmVzc2lvbioqIGlzIGV4dGVuZGVkIHRvICoqbG9naXN0aWMgcmVncmVzc2lvbioqIGJ5IGFwcGx5aW5nIHRoZSBzaWdtb2lkIGZ1bmN0aW9uIHRvIHRoZSBvdXRwdXQuDQotIFRoZSBzaWdtb2lkIGZ1bmN0aW9uIGVuc3VyZXMgdGhlIG91dHB1dCBpcyBiZXR3ZWVuIDAgYW5kIDEsIG1ha2luZyBpdCBzdWl0YWJsZSBmb3IgKipiaW5hcnkgY2xhc3NpZmljYXRpb24qKiB0YXNrcy4NCi0gTG9naXN0aWMgcmVncmVzc2lvbiBwcmVkaWN0cyB0aGUgKipwcm9iYWJpbGl0eSoqIG9mIGEgYmluYXJ5IG91dGNvbWUgKGUuZy4sIDAgb3IgMSksIHdoZXJlIHRoZSBkZWNpc2lvbiBib3VuZGFyeSBpcyB0eXBpY2FsbHkgc2V0IGF0IDAuNS4NCg0KDQojIyMgKipTdW1tYXJ5IGFuZCBFeHBhbnNpb246IFVuZGVyc3RhbmRpbmcgdGhlIFNpZ21vaWQgRnVuY3Rpb24gaW4gTG9naXN0aWMgUmVncmVzc2lvbioqDQoNClRoZSBzaWdtb2lkIGZ1bmN0aW9uIGlzIGEga2V5IG1hdGhlbWF0aWNhbCB0b29sIGluIHRyYW5zZm9ybWluZyBsaW5lYXIgcmVncmVzc2lvbiBpbnRvIGxvZ2lzdGljIHJlZ3Jlc3Npb24uIEl0IG1hcHMgY29udGludW91cyBpbnB1dCB2YWx1ZXMgaW50byBhIHJhbmdlIGJldHdlZW4gMCBhbmQgMSwgbWFraW5nIGl0IHN1aXRhYmxlIGZvciBiaW5hcnkgY2xhc3NpZmljYXRpb24gdGFza3MuIEhlcmUncyBhIGJyZWFrZG93biBhbmQgZGVlcGVyIGV4cGxhbmF0aW9uIG9mIGl0cyBwcm9wZXJ0aWVzIGFuZCBhcHBsaWNhdGlvbnM6DQoNCi0tLQ0KDQojIyMgKioxLiBXaGF0IERvZXMgdGhlIFNpZ21vaWQgRnVuY3Rpb24gRG8/KioNCi0gKipQdXJwb3NlKio6IFRoZSBzaWdtb2lkIGZ1bmN0aW9uIHRyYW5zZm9ybXMgdGhlIG91dHB1dCBvZiBhIGxpbmVhciBlcXVhdGlvbiBpbnRvIGEgcHJvYmFiaWxpdHkgYmV0d2VlbiAwIGFuZCAxLg0KLSAqKkVxdWF0aW9uKio6IA0KICBcWw0KICB5ID0gXGZyYWN7MX17MSArIGVeey1cc3VtIG1faXhfaX19DQogIFxdDQogIC0gXChtX2lcKTogQ29lZmZpY2llbnRzIChzbG9wZXMpIGZvciBlYWNoIGlucHV0IGZlYXR1cmUgXCh4X2lcKS4NCiAgLSBcKFxzdW0gbV9peF9pXCk6IFRoZSBsaW5lYXIgY29tYmluYXRpb24gb2YgZmVhdHVyZXMgYW5kIGNvZWZmaWNpZW50cy4NCiAgLSBcKGVcKTogVGhlIGV4cG9uZW50aWFsIGZ1bmN0aW9uLg0KDQotICoqQmVoYXZpb3IqKjoNCiAgLSBGb3IgbGFyZ2UgcG9zaXRpdmUgdmFsdWVzIG9mIFwoXHN1bSBtX2l4X2lcKSwgdGhlIG91dHB1dCBhcHByb2FjaGVzIDEuDQogIC0gRm9yIGxhcmdlIG5lZ2F0aXZlIHZhbHVlcyBvZiBcKFxzdW0gbV9peF9pXCksIHRoZSBvdXRwdXQgYXBwcm9hY2hlcyAwLg0KICAtIEZvciB2YWx1ZXMgbmVhciAwLCB0aGUgb3V0cHV0IGlzIGFwcHJveGltYXRlbHkgMC41Lg0KDQpUaGlzIHRyYW5zZm9ybWF0aW9uIGNyZWF0ZXMgYW4gKipTLXNoYXBlZCBjdXJ2ZSoqIChzaWdtb2lkIGN1cnZlKSwgd2hpY2ggaXMgaWRlYWwgZm9yIG1vZGVsaW5nIHByb2JhYmlsaXRpZXMuDQoNCi0tLQ0KDQojIyMgKioyLiBSZWxhdGlvbnNoaXAgdG8gQmluYXJ5IENsYXNzaWZpY2F0aW9uKioNCi0gKipCaW5hcnkgT3V0cHV0cyoqOiBMb2dpc3RpYyByZWdyZXNzaW9uIHByZWRpY3RzIHByb2JhYmlsaXRpZXMgZm9yIHR3byBjbGFzc2VzOg0KICAtIENsYXNzIDE6IFByb2JhYmlsaXR5IGNsb3NlIHRvIDEuDQogIC0gQ2xhc3MgMDogUHJvYmFiaWxpdHkgY2xvc2UgdG8gMC4NCi0gKipUaHJlc2hvbGRpbmcqKjoNCiAgLSBBIGRlZmF1bHQgdGhyZXNob2xkIG9mIFwoeSA9IDAuNVwpIGlzIG9mdGVuIHVzZWQ6DQogICAgLSBcKHkgXGdlcSAwLjVcKTogQXNzaWduIHRvIENsYXNzIDEuDQogICAgLSBcKHkgPCAwLjVcKTogQXNzaWduIHRvIENsYXNzIDAuDQogIC0gSG93ZXZlciwgdGhlIHRocmVzaG9sZCBjYW4gYmUgYWRqdXN0ZWQgYmFzZWQgb24gdGhlIHByb2JsZW0gKGUuZy4sIGZyYXVkIGRldGVjdGlvbiwgbWVkaWNhbCBkaWFnbm9zaXMpIHRvIGJhbGFuY2Ugc2Vuc2l0aXZpdHkgYW5kIHNwZWNpZmljaXR5Lg0KDQotLS0NCg0KIyMjICoqMy4gUHJvcGVydGllcyBvZiB0aGUgU2lnbW9pZCBGdW5jdGlvbioqDQotICoqT3V0cHV0IEJlaGF2aW9yKio6DQogIC0gV2hlbiBcKFxzdW0gbV9peF9pXCkgaXMgdmVyeSBsYXJnZSBhbmQgcG9zaXRpdmU6DQogICAgLSBcKGVeey1cdGV4dHtsYXJnZSBwb3NpdGl2ZSBudW1iZXJ9fSBcdG8gMFwpLCBzbyBcKHkgXHRvIDFcKS4NCiAgLSBXaGVuIFwoXHN1bSBtX2l4X2lcKSBpcyB2ZXJ5IGxhcmdlIGFuZCBuZWdhdGl2ZToNCiAgICAtIFwoZV57LVx0ZXh0e2xhcmdlIG5lZ2F0aXZlIG51bWJlcn19IFx0byBcaW5mdHlcKSwgc28gXCh5IFx0byAwXCkuDQotICoqU21vb3RoIFRyYW5zaXRpb24qKjoNCiAgLSBUaGUgc2lnbW9pZCBmdW5jdGlvbiBzbW9vdGhseSB0cmFuc2l0aW9ucyBmcm9tIDAgdG8gMSwgYXZvaWRpbmcgYWJydXB0IGNoYW5nZXMgbGlrZSBhIHN0ZXAgZnVuY3Rpb24uDQogIC0gVGhpcyBzbW9vdGhuZXNzIGFsbG93cyB0aGUgZnVuY3Rpb24gdG8gb3V0cHV0IHByb2JhYmlsaXRpZXMsIHdoaWNoIGNhbiB0aGVuIGJlIHRocmVzaG9sZGVkIGZvciBjbGFzc2lmaWNhdGlvbi4NCg0KLS0tDQoNCiMjIyAqKjQuIEFkanVzdGluZyB0aGUgVGhyZXNob2xkKioNCi0gKipEZWZhdWx0IFRocmVzaG9sZCoqOiBcKHkgPSAwLjVcKS4NCi0gKipDdXN0b20gVGhyZXNob2xkcyoqOg0KICAtIExvd2VyIHRocmVzaG9sZHMgKGUuZy4sIFwoeSA9IDAuMlwpKSBpbmNyZWFzZSBzZW5zaXRpdml0eSwgdXNlZnVsIGZvciBkZXRlY3RpbmcgcmFyZSBldmVudHMgbGlrZSBmcmF1ZC4NCiAgLSBIaWdoZXIgdGhyZXNob2xkcyAoZS5nLiwgXCh5ID0gMC45XCkpIGluY3JlYXNlIHNwZWNpZmljaXR5LCB1c2VmdWwgZm9yIGhpZ2gtY29uZmlkZW5jZSBkZWNpc2lvbnMuDQotICoqSW1wb3J0YW5jZSBvZiBUaHJlc2hvbGRzKio6DQogIC0gQ2hvb3NpbmcgdGhlIHJpZ2h0IHRocmVzaG9sZCBpcyBjcml0aWNhbCBmb3IgYmFsYW5jaW5nIGZhbHNlIHBvc2l0aXZlcyBhbmQgZmFsc2UgbmVnYXRpdmVzLCBkZXBlbmRpbmcgb24gdGhlIGFwcGxpY2F0aW9uJ3MgcmVxdWlyZW1lbnRzLg0KDQotLS0NCg0KIyMjICoqNS4gV2h5IHRoZSBTaWdtb2lkIEZ1bmN0aW9uPyoqDQotICoqU2ltdWxhdGVzIGEgU3RlcCBGdW5jdGlvbioqOg0KICAtIEF0IGV4dHJlbWUgdmFsdWVzLCB0aGUgc2lnbW9pZCBmdW5jdGlvbiBiZWhhdmVzIGxpa2UgYSBzdGVwIGZ1bmN0aW9uLCBwcm9kdWNpbmcgb3V0cHV0cyBjbG9zZSB0byAwIG9yIDEuDQotICoqTWF0aGVtYXRpY2FsIFNpbXBsaWNpdHkqKjoNCiAgLSBEZXNwaXRlIGludm9sdmluZyBleHBvbmVudGlhbHMsIHRoZSBzaWdtb2lkIGZ1bmN0aW9uIGhhcyBwcm9wZXJ0aWVzIHRoYXQgc2ltcGxpZnkgY2FsY3VsYXRpb25zLCBtYWtpbmcgaXQgY29tcHV0YXRpb25hbGx5IGVmZmljaWVudC4NCi0gKipQcm9iYWJpbGlzdGljIEludGVycHJldGF0aW9uKio6DQogIC0gVGhlIG91dHB1dCBvZiB0aGUgc2lnbW9pZCBmdW5jdGlvbiBjYW4gYmUgaW50ZXJwcmV0ZWQgYXMgdGhlIHByb2JhYmlsaXR5IG9mIGJlbG9uZ2luZyB0byBDbGFzcyAxLg0KDQotLS0NCg0KIyMjICoqNi4gUHJhY3RpY2FsIENvbnNpZGVyYXRpb25zKioNCi0gKipGcmFtaW5nIHRoZSBQcm9ibGVtKio6DQogIC0gTG9naXN0aWMgcmVncmVzc2lvbiBpcyBmcmFtZWQgc3VjaCB0aGF0IHBvc2l0aXZlIHZhbHVlcyBvZiBcKFxzdW0gbV9peF9pXCkgY29ycmVzcG9uZCB0byBDbGFzcyAxLCBhbmQgbmVnYXRpdmUgdmFsdWVzIGNvcnJlc3BvbmQgdG8gQ2xhc3MgMC4NCi0gKipBcHBsaWNhdGlvbnMqKjoNCiAgLSBMb2dpc3RpYyByZWdyZXNzaW9uIGlzIHdpZGVseSB1c2VkIGluIGFwcGxpY2F0aW9ucyBsaWtlIGZyYXVkIGRldGVjdGlvbiwgbWVkaWNhbCBkaWFnbm9zaXMsIGFuZCBzcGFtIGNsYXNzaWZpY2F0aW9uLg0KDQotLS0NCg0KIyMjICoqQ29uY2x1c2lvbioqDQpUaGUgc2lnbW9pZCBmdW5jdGlvbiBpcyB0aGUgY29ybmVyc3RvbmUgb2YgbG9naXN0aWMgcmVncmVzc2lvbiwgZW5hYmxpbmcgdGhlIHRyYW5zZm9ybWF0aW9uIG9mIGNvbnRpbnVvdXMgbGluZWFyIG91dHB1dHMgaW50byBwcm9iYWJpbGl0aWVzIHN1aXRhYmxlIGZvciBiaW5hcnkgY2xhc3NpZmljYXRpb24uIEl0cyBzbW9vdGgsIFMtc2hhcGVkIGN1cnZlIGFuZCBhYmlsaXR5IHRvIHNpbXVsYXRlIHN0ZXAtbGlrZSBiZWhhdmlvciBtYWtlIGl0IGlkZWFsIGZvciBtb2RlbGluZyBkaXNjcmV0ZSBvdXRjb21lcy4gQnkgYWRqdXN0aW5nIHRocmVzaG9sZHMgYW5kIGxldmVyYWdpbmcgaXRzIG1hdGhlbWF0aWNhbCBwcm9wZXJ0aWVzLCB0aGUgc2lnbW9pZCBmdW5jdGlvbiBwcm92aWRlcyBmbGV4aWJpbGl0eSBhbmQgcHJlY2lzaW9uIGluIGEgdmFyaWV0eSBvZiByZWFsLXdvcmxkIGFwcGxpY2F0aW9ucy4NCg0KIyMjICoqU3VtbWFyeSBvZiBMb2dhcml0aG1pYyBMb3NzIGFuZCBJdHMgRGVyaXZhdGlvbioqDQoNCkxvZ2FyaXRobWljIExvc3MgKExvZyBMb3NzKSBpcyBhIGNyaXRpY2FsIGxvc3MgZnVuY3Rpb24gdXNlZCBpbiBjbGFzc2lmaWNhdGlvbiBwcm9ibGVtcywgcGFydGljdWxhcmx5IGZvciBiaW5hcnkgY2xhc3NpZmljYXRpb24gdGFza3MuIEl0IGV2YWx1YXRlcyB0aGUgcGVyZm9ybWFuY2Ugb2YgYSBjbGFzc2lmaWNhdGlvbiBtb2RlbCBieSBwZW5hbGl6aW5nIHByZWRpY3Rpb25zIHRoYXQgZGV2aWF0ZSBmcm9tIHRoZSB0cnVlIHRhcmdldCBwcm9iYWJpbGl0aWVzLiBIZXJl4oCZcyBhIGJyZWFrZG93biBvZiB0aGUga2V5IGlkZWFzOg0KDQoxLiAqKkxvc3MgRnVuY3Rpb25zIGluIFJlZ3Jlc3Npb24qKjoNCiAgIC0gRm9yIHJlZ3Jlc3Npb24sIHdlIHR5cGljYWxseSB1c2UgKipNZWFuIFNxdWFyZWQgRXJyb3IgKE1TRSkqKiBvciAqKk1lYW4gQWJzb2x1dGUgRXJyb3IgKE1BRSkqKiB0byBtaW5pbWl6ZSB0aGUgdmVydGljYWwgZGlzdGFuY2UgYmV0d2VlbiBwcmVkaWN0aW9ucyAoXCggXGhhdHt5fSBcKSkgYW5kIHRydWUgdGFyZ2V0cyAoXCggeSBcKSkuDQogICAtIFRoZXNlIG1lYXN1cmUgZXJyb3IgdXNpbmcgY29udGludW91cyBkaXN0YW5jZXMgYmV0d2VlbiBwcmVkaWN0ZWQgYW5kIGFjdHVhbCB2YWx1ZXMuDQoNCjIuICoqQ2hhbGxlbmdlcyBpbiBDbGFzc2lmaWNhdGlvbioqOg0KICAgLSBDbGFzc2lmaWNhdGlvbiBpbnZvbHZlcyBkaXNjcmV0ZSBsYWJlbHMsIHN1Y2ggYXMgXCggeSA9IDAgXCkgb3IgXCggeSA9IDEgXCkuDQogICAtIFByZWRpY3Rpb25zIGFyZSBwcm9iYWJpbGlzdGljLCBlLmcuLCBcKCBwKHk9MSB8IHgpIFwpLCBkZXJpdmVkIGZyb20gbW9kZWxzIGxpa2UgbG9naXN0aWMgcmVncmVzc2lvbiB3aXRoIGEgKipzaWdtb2lkIGFjdGl2YXRpb24gZnVuY3Rpb24qKi4NCg0KMy4gKipMb2cgTG9zcyBGdW5jdGlvbioqOg0KICAgLSBUaGUgc2lnbW9pZCBmdW5jdGlvbiBvdXRwdXRzIHByb2JhYmlsaXRpZXMgKFwoIHAgXCkpIGJldHdlZW4gMCBhbmQgMToNCiAgICAgXFsNCiAgICAgcCA9IFxzaWdtYSh6KSA9IFxmcmFjezF9ezEgKyBlXnsten19DQogICAgIFxdDQogICAtIFRoZSBsb2cgbG9zcyBmdW5jdGlvbiBtZWFzdXJlcyBob3cgY2xvc2UgdGhlc2UgcHJvYmFiaWxpdGllcyBhcmUgdG8gdGhlIHRydWUgbGFiZWxzIHVzaW5nIHRoZSBuYXR1cmFsIGxvZ2FyaXRobSAoXCggXGxuIFwpKS4NCg0KNC4gKipNYXRoZW1hdGljYWwgRGVyaXZhdGlvbioqOg0KICAgLSBMb2cgbG9zcyBmb3IgXCggeT0xIFwpOg0KICAgICBcWw0KICAgICBcdGV4dHtMb3NzfSA9IC1cbG4ocCkNCiAgICAgXF0NCiAgIC0gTG9nIGxvc3MgZm9yIFwoIHk9MCBcKToNCiAgICAgXFsNCiAgICAgXHRleHR7TG9zc30gPSAtXGxuKDEtcCkNCiAgICAgXF0NCiAgIC0gQ29tYmluZWQsIGZvciBnZW5lcmFsIFwoIHkgXCkgKGJpbmFyeSk6DQogICAgIFxbDQogICAgIFx0ZXh0e0xvZyBMb3NzfSA9IC0gXGJpZyggeSBcbG4ocCkgKyAoMS15KSBcbG4oMS1wKSBcYmlnKQ0KICAgICBcXQ0KICAgLSBIZXJlLCBcKCBwIFwpIGlzIHRoZSBtb2RlbCdzIHByZWRpY3RlZCBwcm9iYWJpbGl0eSBmb3IgXCggeT0xIFwpLCBhbmQgXCggMS1wIFwpIGZvciBcKCB5PTAgXCkuDQoNCjUuICoqSW50dWl0aW9uKio6DQogICAtICoqUGVuYWxpemluZyBpbmNvcnJlY3QgcHJlZGljdGlvbnMqKjogTG9nYXJpdGhtcyBhbXBsaWZ5IHBlbmFsdGllcyBmb3IgcHJlZGljdGlvbnMgdGhhdCBhcmUgZmFyIGZyb20gdGhlIHRydWUgbGFiZWwuDQogICAtIEEgcHJlZGljdGlvbiBvZiBcKCBwPTAuOTkgXCkgZm9yIFwoIHk9MSBcKSBpbmN1cnMgYSBzbWFsbCBsb3NzLCBidXQgXCggcD0wLjAxIFwpIGluY3VycyBhIGxhcmdlIGxvc3MuDQogICAtIFRoZSBsb2cgbG9zcyBmdW5jdGlvbiBpcyAqKmNvbnZleCoqLCBlbnN1cmluZyBhIHVuaXF1ZSBnbG9iYWwgbWluaW11bS4NCg0KNi4gKipWaXN1YWxpemF0aW9uKio6DQogICAtIFRoZSBsb2cgbG9zcyBjdXJ2ZXMgZm9yIFwoIHk9MSBcKSBhbmQgXCggeT0wIFwpIGFyZSBtaXJyb3IgaW1hZ2VzLCByZWZsZWN0aW5nIHRoZWlyIHN5bW1ldHJ5Lg0KICAgLSBUaGUgdG90YWwgbG9zcyBmdW5jdGlvbiBmb3JtcyBhIGNvbnZleCBzaGFwZSwgc2ltcGxpZnlpbmcgb3B0aW1pemF0aW9uLg0KDQotLS0NCg0KIyMjICoqTWF0aGVtYXRpY2FsIE5vdGF0aW9uIGluIENvZGUqKg0KDQojIyMjICoqUHl0aG9uIENvZGUgRXhhbXBsZSoqDQpCZWxvdyBpcyBhIFB5dGhvbiBpbXBsZW1lbnRhdGlvbiBvZiB0aGUgbG9nIGxvc3MgZnVuY3Rpb24gZm9yIGJpbmFyeSBjbGFzc2lmaWNhdGlvbi4NCg0KYGBgcHl0aG9uDQppbXBvcnQgbnVtcHkgYXMgbnANCg0KZGVmIHNpZ21vaWQoeik6DQogICAgIiIiQ29tcHV0ZSB0aGUgc2lnbW9pZCBvZiB6LiIiIg0KICAgIHJldHVybiAxIC8gKDEgKyBucC5leHAoLXopKQ0KDQpkZWYgbG9nX2xvc3MoeV90cnVlLCB5X3ByZWQpOg0KICAgICIiIg0KICAgIENvbXB1dGUgdGhlIGxvZyBsb3NzIGZvciBiaW5hcnkgY2xhc3NpZmljYXRpb24uDQogICAgDQogICAgUGFyYW1ldGVyczoNCiAgICAtIHlfdHJ1ZTogVHJ1ZSBsYWJlbHMgKDAgb3IgMSkuDQogICAgLSB5X3ByZWQ6IFByZWRpY3RlZCBwcm9iYWJpbGl0aWVzIChiZXR3ZWVuIDAgYW5kIDEpLg0KICAgIA0KICAgIFJldHVybnM6DQogICAgLSBBdmVyYWdlIGxvZyBsb3NzLg0KICAgICIiIg0KICAgIGVwc2lsb24gPSAxZS0xNSAgIyBBdm9pZCBsb2coMCkgYnkgY2xpcHBpbmcgcHJlZGljdGlvbnMNCiAgICB5X3ByZWQgPSBucC5jbGlwKHlfcHJlZCwgZXBzaWxvbiwgMSAtIGVwc2lsb24pDQogICAgbG9zcyA9IC1ucC5tZWFuKHlfdHJ1ZSAqIG5wLmxvZyh5X3ByZWQpICsgKDEgLSB5X3RydWUpICogbnAubG9nKDEgLSB5X3ByZWQpKQ0KICAgIHJldHVybiBsb3NzDQoNCiMgRXhhbXBsZSB1c2FnZQ0KeV90cnVlID0gbnAuYXJyYXkoWzEsIDAsIDEsIDBdKSAgIyBUcnVlIGxhYmVscw0KeV9wcmVkID0gbnAuYXJyYXkoWzAuOSwgMC4xLCAwLjgsIDAuMl0pICAjIFByZWRpY3RlZCBwcm9iYWJpbGl0aWVzDQoNCnByaW50KCJMb2cgTG9zczoiLCBsb2dfbG9zcyh5X3RydWUsIHlfcHJlZCkpDQpgYGANCg0KLS0tDQoNCiMjIyAqKlZpc3VhbGl6YXRpb24gaW4gUHl0aG9uKioNCg0KVGhlIGZvbGxvd2luZyBQeXRob24gY29kZSB2aXN1YWxpemVzIGxvZyBsb3NzIGZvciBcKCB5PTEgXCkgYW5kIFwoIHk9MCBcKS4NCg0KYGBgcHl0aG9uDQppbXBvcnQgbWF0cGxvdGxpYi5weXBsb3QgYXMgcGx0DQoNCiMgUHJvYmFiaWxpdGllcyBmcm9tIDAgdG8gMQ0KcCA9IG5wLmxpbnNwYWNlKDAuMDEsIDAuOTksIDEwMCkNCg0KIyBMb2cgbG9zcyBmb3IgeT0xIGFuZCB5PTANCmxvc3NfeTEgPSAtbnAubG9nKHApDQpsb3NzX3kwID0gLW5wLmxvZygxIC0gcCkNCg0KIyBQbG90DQpwbHQuZmlndXJlKGZpZ3NpemU9KDgsIDYpKQ0KcGx0LnBsb3QocCwgbG9zc195MSwgbGFiZWw9J0xvc3MgKHk9MSknLCBjb2xvcj0nYmx1ZScpDQpwbHQucGxvdChwLCBsb3NzX3kwLCBsYWJlbD0nTG9zcyAoeT0wKScsIGNvbG9yPSdvcmFuZ2UnKQ0KcGx0LnRpdGxlKCJMb2dhcml0aG1pYyBMb3NzIGZvciBCaW5hcnkgQ2xhc3NpZmljYXRpb24iKQ0KcGx0LnhsYWJlbCgiUHJlZGljdGVkIFByb2JhYmlsaXR5IChwKSIpDQpwbHQueWxhYmVsKCJMb2cgTG9zcyIpDQpwbHQubGVnZW5kKCkNCnBsdC5ncmlkKCkNCnBsdC5zaG93KCkNCmBgYA0KDQotLS0NCg0KIyMjICoqU3VtbWFyeSBvZiBLZXkgSW5zaWdodHMqKg0KLSAqKkxvZyBMb3NzKiogY29tYmluZXMgdGVybXMgZm9yIFwoIHk9MSBcKSBhbmQgXCggeT0wIFwpIGludG8gYSBzaW5nbGUsIHN5bW1ldHJpYyBmdW5jdGlvbi4NCi0gSXQgcGVuYWxpemVzIGluY29ycmVjdCBwcmVkaWN0aW9ucyBtb3JlIGFzIHRoZXkgZGV2aWF0ZSBmcm9tIHRoZSB0cnVlIGNsYXNzIHByb2JhYmlsaXR5Lg0KLSBUaGUgY29udmV4aXR5IG9mIGxvZyBsb3NzIGVuc3VyZXMgb3B0aW1pemF0aW9uIGlzIGZlYXNpYmxlIHdpdGggbWV0aG9kcyBsaWtlIGdyYWRpZW50IGRlc2NlbnQuDQoNClRoaXMgZm91bmRhdGlvbiBtYWtlcyBsb2cgbG9zcyBhIHN0YW5kYXJkIGZvciBjbGFzc2lmaWNhdGlvbiB0YXNrcyBhbmQgYSBjcnVjaWFsIG1ldHJpYyBmb3IgdHJhaW5pbmcgYW5kIGV2YWx1YXRpbmcgbW9kZWxzIGxpa2UgbG9naXN0aWMgcmVncmVzc2lvbiBhbmQgbmV1cmFsIG5ldHdvcmtzLg0KDQojIyMgU3VtbWFyeSBhbmQgRXhwYW5zaW9uOiBMb2dhcml0aG1pYyBMb3NzIChMb2cgTG9zcykgaW4gTG9naXN0aWMgUmVncmVzc2lvbg0KDQpMb2dhcml0aG1pYyBsb3NzLCBvciBsb2cgbG9zcywgaXMgYSBrZXkgbG9zcyBmdW5jdGlvbiB1c2VkIGluIGxvZ2lzdGljIHJlZ3Jlc3Npb24gdG8gZXZhbHVhdGUgdGhlIHBlcmZvcm1hbmNlIG9mIGEgbW9kZWwgd2hlbiBwcmVkaWN0aW5nIHByb2JhYmlsaXRpZXMgZm9yICoqYmluYXJ5IGNsYXNzaWZpY2F0aW9uIHRhc2tzKiouIEl0IG1lYXN1cmVzIGhvdyBmYXIgdGhlIHByZWRpY3RlZCBwcm9iYWJpbGl0aWVzIGFyZSBmcm9tIHRoZSBhY3R1YWwgdGFyZ2V0IHZhbHVlcyAoMCBvciAxKS4NCg0KLS0tDQoNCiMjIyAqKjEuIFdoeSBOb3QgVXNlIE1lYW4gU3F1YXJlZCBFcnJvciAoTVNFKT8qKg0KLSAqKkxpbmVhciBSZWdyZXNzaW9uKio6IEluIGxpbmVhciByZWdyZXNzaW9uLCB3ZSB1c2UgTVNFIG9yIE1lYW4gQWJzb2x1dGUgRXJyb3IgKE1BRSkgdG8gbWVhc3VyZSB0aGUgZGlzdGFuY2UgYmV0d2VlbiBwcmVkaWN0aW9ucyBhbmQgdGFyZ2V0cy4gVGhlc2UgbWV0aG9kcyB3b3JrIHdlbGwgZm9yIGNvbnRpbnVvdXMgb3V0cHV0cy4NCi0gKipDYXRlZ29yaWNhbCBEYXRhKio6IEZvciBiaW5hcnkgY2xhc3NpZmljYXRpb24sIHRoZSB0YXJnZXQgdmFsdWVzIGFyZSBlaXRoZXIgMCBvciAxLiBVc2luZyBNU0UgaXMgbm90IGlkZWFsIGJlY2F1c2UgaXQgZG9lc24ndCBhY2NvdW50IGZvciB0aGUgcHJvYmFiaWxpc3RpYyBuYXR1cmUgb2YgdGhlIHByZWRpY3Rpb25zIGluIGxvZ2lzdGljIHJlZ3Jlc3Npb24uDQotICoqTmVlZCBmb3IgTG9nIExvc3MqKjogTG9nIGxvc3MgaXMgZGVzaWduZWQgc3BlY2lmaWNhbGx5IGZvciBjbGFzc2lmaWNhdGlvbiB0YXNrcywgcGVuYWxpemluZyBpbmNvcnJlY3QgcHJlZGljdGlvbnMgbW9yZSBlZmZlY3RpdmVseSBieSB1c2luZyBwcm9iYWJpbGl0aWVzLg0KDQotLS0NCg0KIyMjICoqMi4gTG9nYXJpdGhtaWMgTG9zcyBGdW5jdGlvbioqDQpUaGUgbG9nIGxvc3MgZnVuY3Rpb24gY29tYmluZXMgdHdvIGNhc2VzOiB3aGVuIHRoZSB0YXJnZXQgXCh5ID0gMVwpIGFuZCB3aGVuIFwoeSA9IDBcKS4gSXQgaXMgZGVmaW5lZCBhczoNCg0KXFsNClx0ZXh0e0xvZyBMb3NzfSA9IC0gXGxlZnRbIHkgXGNkb3QgXGxvZyhwKSArICgxIC0geSkgXGNkb3QgXGxvZygxIC0gcCkgXHJpZ2h0XQ0KXF0NCg0KLSAqKktleSBDb21wb25lbnRzKio6DQogIC0gXChwXCk6IFByZWRpY3RlZCBwcm9iYWJpbGl0eSAob3V0cHV0IG9mIHRoZSBzaWdtb2lkIGZ1bmN0aW9uKS4NCiAgLSBcKHlcKTogQWN0dWFsIHRhcmdldCB2YWx1ZSAoMCBvciAxKS4NCiAgLSBcKFxsb2dcKTogTmF0dXJhbCBsb2dhcml0aG0gKGJhc2UgXChlXCkpLg0KDQotICoqRXhwbGFuYXRpb24qKjoNCiAgLSBXaGVuIFwoeSA9IDFcKTogVGhlIGZpcnN0IHRlcm0gXCgteSBcY2RvdCBcbG9nKHApXCkgaXMgYWN0aXZlLCBhbmQgdGhlIHNlY29uZCB0ZXJtIGJlY29tZXMgemVyby4NCiAgLSBXaGVuIFwoeSA9IDBcKTogVGhlIHNlY29uZCB0ZXJtIFwoLSgxIC0geSkgXGNkb3QgXGxvZygxIC0gcClcKSBpcyBhY3RpdmUsIGFuZCB0aGUgZmlyc3QgdGVybSBiZWNvbWVzIHplcm8uDQogIC0gVGhpcyBlbnN1cmVzIHRoYXQgb25seSB0aGUgcmVsZXZhbnQgdGVybSBjb250cmlidXRlcyB0byB0aGUgbG9zcyBiYXNlZCBvbiB0aGUgYWN0dWFsIHRhcmdldC4NCg0KLS0tDQoNCiMjIyAqKjMuIEludHVpdGlvbiBCZWhpbmQgTG9nIExvc3MqKg0KLSAqKlBlbmFsdHkgZm9yIEluY29ycmVjdCBQcmVkaWN0aW9ucyoqOg0KICAtIElmIHRoZSBwcmVkaWN0ZWQgcHJvYmFiaWxpdHkgXChwXCkgaXMgY2xvc2UgdG8gdGhlIGFjdHVhbCB0YXJnZXQgXCh5XCksIHRoZSBsb3NzIGlzIHNtYWxsLg0KICAtIElmIFwocFwpIGlzIGZhciBmcm9tIFwoeVwpLCB0aGUgbG9zcyBpcyBsYXJnZS4NCi0gKipCZWhhdmlvcioqOg0KICAtIEZvciBcKHkgPSAxXCksIGlmIFwocFwpIGlzIGNsb3NlIHRvIDEsIFwoLVxsb2cocClcKSBpcyBzbWFsbC4gSWYgXChwXCkgaXMgY2xvc2UgdG8gMCwgXCgtXGxvZyhwKVwpIGJlY29tZXMgdmVyeSBsYXJnZS4NCiAgLSBGb3IgXCh5ID0gMFwpLCBpZiBcKHBcKSBpcyBjbG9zZSB0byAwLCBcKC1cbG9nKDEgLSBwKVwpIGlzIHNtYWxsLiBJZiBcKHBcKSBpcyBjbG9zZSB0byAxLCBcKC1cbG9nKDEgLSBwKVwpIGJlY29tZXMgdmVyeSBsYXJnZS4NCg0KVGhpcyBwZW5hbGl6YXRpb24gZW5jb3VyYWdlcyB0aGUgbW9kZWwgdG8gcHJlZGljdCBwcm9iYWJpbGl0aWVzIGNsb3NlciB0byB0aGUgdHJ1ZSB0YXJnZXQuDQoNCi0tLQ0KDQojIyMgKio0LiBXaHkgVXNlIExvZyBMb3NzPyoqDQotICoqUHJvYmFiaWxpc3RpYyBQcmVkaWN0aW9ucyoqOg0KICAtIExvZ2lzdGljIHJlZ3Jlc3Npb24gb3V0cHV0cyBwcm9iYWJpbGl0aWVzIHVzaW5nIHRoZSBzaWdtb2lkIGZ1bmN0aW9uLiBMb2cgbG9zcyBldmFsdWF0ZXMgdGhlc2UgcHJvYmFiaWxpdGllcyByYXRoZXIgdGhhbiBkaXNjcmV0ZSBwcmVkaWN0aW9ucy4NCi0gKipDb3VwbGVkIENsYXNzZXMqKjoNCiAgLSBTaW5jZSBiaW5hcnkgY2xhc3NpZmljYXRpb24gaW52b2x2ZXMgdHdvIG11dHVhbGx5IGV4Y2x1c2l2ZSBjbGFzc2VzICgwIGFuZCAxKSwgbG9nIGxvc3MgYWNjb3VudHMgZm9yIGJvdGggY2FzZXMgc2ltdWx0YW5lb3VzbHkuDQotICoqQ29udmV4aXR5Kio6DQogIC0gTG9nIGxvc3MgZm9ybXMgYSAqKmNvbnZleCBjdXJ2ZSoqIHdpdGggYSB3ZWxsLWRlZmluZWQgbWluaW11bSwgd2hpY2ggaXMgaWRlYWwgZm9yIG9wdGltaXphdGlvbiBhbGdvcml0aG1zIGxpa2UgZ3JhZGllbnQgZGVzY2VudC4NCg0KLS0tDQoNCiMjIyAqKjUuIFZpc3VhbGl6YXRpb24gb2YgTG9nIExvc3MqKg0KLSAqKkxvc3MgZm9yIFwoeSA9IDFcKSoqOg0KICAtIEFzIFwocCBcdG8gMVwpLCB0aGUgbG9zcyBhcHByb2FjaGVzIDAgKGNvcnJlY3QgcHJlZGljdGlvbikuDQogIC0gQXMgXChwIFx0byAwXCksIHRoZSBsb3NzIGJlY29tZXMgdmVyeSBsYXJnZSAoaW5jb3JyZWN0IHByZWRpY3Rpb24pLg0KLSAqKkxvc3MgZm9yIFwoeSA9IDBcKSoqOg0KICAtIEFzIFwocCBcdG8gMFwpLCB0aGUgbG9zcyBhcHByb2FjaGVzIDAgKGNvcnJlY3QgcHJlZGljdGlvbikuDQogIC0gQXMgXChwIFx0byAxXCksIHRoZSBsb3NzIGJlY29tZXMgdmVyeSBsYXJnZSAoaW5jb3JyZWN0IHByZWRpY3Rpb24pLg0KLSAqKkNvbWJpbmVkIExvc3MqKjoNCiAgLSBUaGUgdG90YWwgbG9nIGxvc3MgY3VydmUgaXMgY29udmV4LCBlbnN1cmluZyBhIHNpbmdsZSBnbG9iYWwgbWluaW11bS4NCg0KLS0tDQoNCiMjIyAqKjYuIFByYWN0aWNhbCBBcHBsaWNhdGlvbnMqKg0KLSAqKlRocmVzaG9sZGluZyoqOg0KICAtIExvZ2lzdGljIHJlZ3Jlc3Npb24gcHJlZGljdHMgcHJvYmFiaWxpdGllcy4gQSB0aHJlc2hvbGQgKGUuZy4sIDAuNSkgaXMgdXNlZCB0byBjbGFzc2lmeSBwcm9iYWJpbGl0aWVzIGludG8gYmluYXJ5IG91dGNvbWVzLg0KICAtIExvZyBsb3NzIGVuc3VyZXMgdGhlIG1vZGVsIGZvY3VzZXMgb24gaW1wcm92aW5nIHByZWRpY3Rpb25zIGNsb3NlIHRvIHRoZSB0aHJlc2hvbGQuDQotICoqTW9kZWwgVHJhaW5pbmcqKjoNCiAgLSBNaW5pbWl6aW5nIGxvZyBsb3NzIGR1cmluZyB0cmFpbmluZyBlbnN1cmVzIHRoZSBtb2RlbCBvdXRwdXRzIHByb2JhYmlsaXRpZXMgdGhhdCBhbGlnbiBjbG9zZWx5IHdpdGggdGhlIHRydWUgbGFiZWxzLg0KDQotLS0NCg0KIyMjICoqNy4gQWR2YW50YWdlcyBvZiBMb2cgTG9zcyoqDQotICoqSGFuZGxlcyBQcm9iYWJpbGl0aWVzKio6IFVubGlrZSBNU0UsIGl0IGV2YWx1YXRlcyBwcm9iYWJpbGlzdGljIHByZWRpY3Rpb25zIGVmZmVjdGl2ZWx5Lg0KLSAqKlBlbmFsaXplcyBDb25maWRlbnQgV3JvbmcgUHJlZGljdGlvbnMqKjogUHJlZGljdGlvbnMgZmFyIGZyb20gdGhlIHRydWUgdGFyZ2V0IGFyZSBwZW5hbGl6ZWQgbW9yZSBoZWF2aWx5Lg0KLSAqKk9wdGltaXphYmxlKio6IFRoZSBjb252ZXggbmF0dXJlIG9mIGxvZyBsb3NzIGVuc3VyZXMgdGhhdCBvcHRpbWl6YXRpb24gYWxnb3JpdGhtcyBjYW4gZmluZCB0aGUgZ2xvYmFsIG1pbmltdW0uDQoNCi0tLQ0KDQojIyMgKipDb25jbHVzaW9uKioNCkxvZyBsb3NzIGlzIGEgY3JpdGljYWwgdG9vbCBmb3IgZXZhbHVhdGluZyBhbmQgdHJhaW5pbmcgbG9naXN0aWMgcmVncmVzc2lvbiBtb2RlbHMuIEJ5IHBlbmFsaXppbmcgaW5jb3JyZWN0IHByZWRpY3Rpb25zIGJhc2VkIG9uIHByb2JhYmlsaXRpZXMsIGl0IGVuc3VyZXMgdGhlIG1vZGVsIG91dHB1dHMgcHJvYmFiaWxpdGllcyB0aGF0IGFyZSBib3RoIGFjY3VyYXRlIGFuZCByZWxpYWJsZS4gSXRzIG1hdGhlbWF0aWNhbCBwcm9wZXJ0aWVzIGFuZCBpbnR1aXRpdmUgYmVoYXZpb3IgbWFrZSBpdCBpZGVhbCBmb3IgYmluYXJ5IGNsYXNzaWZpY2F0aW9uIHRhc2tzLg0KDQojIyMgKipNaW5pbWl6aW5nIExvZyBMb3NzOiBTdW1tYXJ5IGFuZCBEZXRhaWxlZCBFeHBhbnNpb24qKg0KDQotLS0NCg0KIyMjICoqQ29uY2VwdHVhbCBPdmVydmlldyoqDQoNCk1pbmltaXppbmcgKipsb2dhcml0aG1pYyBsb3NzIChsb2cgbG9zcykqKiBpcyBhIGNvcmUgc3RlcCBpbiB0cmFpbmluZyBsb2dpc3RpYyByZWdyZXNzaW9uIG1vZGVscy4gQSBzbWFsbGVyIGxvZyBsb3NzIGluZGljYXRlcyB0aGF0IHRoZSBtb2RlbCdzIHByZWRpY3RlZCBwcm9iYWJpbGl0aWVzIGNsb3NlbHkgYWxpZ24gd2l0aCB0aGUgdHJ1ZSBjbGFzcyBsYWJlbHMsIHJlc3VsdGluZyBpbiBiZXR0ZXIgY2xhc3NpZmljYXRpb24gcGVyZm9ybWFuY2UuDQoNCiMjIyMgKipLZXkgQ29tcG9uZW50cyoqDQoxLiAqKkxvZyBMb3NzIEZ1bmN0aW9uOioqDQogICAtIFRoZSBsb2cgbG9zcyBcKCBKIFwpIGZvciBiaW5hcnkgY2xhc3NpZmljYXRpb24gaXM6DQogICAgIFxbDQogICAgIEooXG1hdGhiZnt3fSkgPSAtXGZyYWN7MX17Tn0gXHN1bV97aT0xfV57Tn0gXEJpZ1sgeV9pIFxsb2cocF9pKSArICgxIC0geV9pKSBcbG9nKDEgLSBwX2kpIFxCaWddDQogICAgIFxdDQogICAgIHdoZXJlOg0KICAgICAtIFwoIE4gXCkgPSBudW1iZXIgb2YgZGF0YSBwb2ludHMsDQogICAgIC0gXCggeV9pIFwpID0gdHJ1ZSBsYWJlbCAoXCggMCBcKSBvciBcKCAxIFwpKSwNCiAgICAgLSBcKCBwX2kgPSBcc2lnbWEoel9pKSA9IFxmcmFjezF9ezEgKyBlXnstel9pfX0gXCksIHRoZSBwcmVkaWN0ZWQgcHJvYmFiaWxpdHkgZm9yIFwoIHk9MSBcKSwNCiAgICAgLSBcKCB6X2kgPSBcbWF0aGJme3d9XlQgXG1hdGhiZnt4fV9pIFwpLCB0aGUgbGluZWFyIGNvbWJpbmF0aW9uIG9mIHdlaWdodHMgKFwoIFxtYXRoYmZ7d30gXCkpIGFuZCBmZWF0dXJlcyAoXCggXG1hdGhiZnt4fV9pIFwpKS4NCg0KMi4gKipQcmVkaWN0aW9uIEZ1bmN0aW9uIChTaWdtb2lkKToqKg0KICAgLSBUaGUgc2lnbW9pZCBhY3RpdmF0aW9uIHNxdWFzaGVzIHRoZSBsaW5lYXIgY29tYmluYXRpb24gb2YgaW5wdXRzIGludG8gYSBwcm9iYWJpbGl0eToNCiAgICAgXFsNCiAgICAgXHNpZ21hKHopID0gXGZyYWN7MX17MSArIGVeey16fX0NCiAgICAgXF0NCiAgICAgLSBFbnN1cmVzIFwoIDAgPCBwIDwgMSBcKSwgc3VpdGFibGUgZm9yIHByb2JhYmlsaXR5IGVzdGltYXRpb24uDQoNCjMuICoqT3B0aW1pemF0aW9uIEdvYWw6KioNCiAgIC0gTWluaW1pemUgXCggSihcbWF0aGJme3d9KSBcKSBieSBhZGp1c3RpbmcgdGhlIHdlaWdodHMgXCggXG1hdGhiZnt3fSBcKSwgd2hpY2ggY29udHJvbCB0aGUgZGVjaXNpb24gYm91bmRhcnkgb2YgdGhlIGNsYXNzaWZpZXIuDQoNCi0tLQ0KDQojIyMgKipNYXRoZW1hdGljcyBvZiBNaW5pbWl6aW5nIExvZyBMb3NzKioNCg0KVG8gbWluaW1pemUgXCggSihcbWF0aGJme3d9KSBcKSwgd2UgYXBwbHkgKipncmFkaWVudCBkZXNjZW50Kio6DQoxLiAqKkdyYWRpZW50IG9mIExvZyBMb3NzIHcuci50IFdlaWdodHM6KioNCiAgIC0gVGhlIHBhcnRpYWwgZGVyaXZhdGl2ZSBvZiBcKCBKIFwpIHdpdGggcmVzcGVjdCB0byBcKCBcbWF0aGJme3d9IFwpIGlzOg0KICAgICBcWw0KICAgICBcZnJhY3tccGFydGlhbCBKfXtccGFydGlhbCBcbWF0aGJme3d9fSA9IFxmcmFjezF9e059IFxzdW1fe2k9MX1ee059IChcc2lnbWEoel9pKSAtIHlfaSkgXG1hdGhiZnt4fV9pDQogICAgIFxdDQogICAgIC0gSW50dWl0aW9uOg0KICAgICAgIC0gXCggXHNpZ21hKHpfaSkgLSB5X2kgXCk6IFRoZSBwcmVkaWN0aW9uIGVycm9yLg0KICAgICAgIC0gXCggXG1hdGhiZnt4fV9pIFwpOiBGZWF0dXJlIHZlY3RvciBpbmZsdWVuY2VzIHdlaWdodCB1cGRhdGVzLg0KDQoyLiAqKkdyYWRpZW50IERlc2NlbnQgVXBkYXRlIFJ1bGU6KioNCiAgIC0gVXBkYXRlIHdlaWdodHMgaXRlcmF0aXZlbHk6DQogICAgIFxbDQogICAgIFxtYXRoYmZ7d30gXGxlZnRhcnJvdyBcbWF0aGJme3d9IC0gXGV0YSBcZnJhY3tccGFydGlhbCBKfXtccGFydGlhbCBcbWF0aGJme3d9fQ0KICAgICBcXQ0KICAgICAtIFwoIFxldGEgXCk6IExlYXJuaW5nIHJhdGUgY29udHJvbHMgc3RlcCBzaXplLg0KDQotLS0NCg0KIyMjICoqQ29kaW5nIFJlcHJlc2VudGF0aW9uKioNCg0KSGVyZeKAmXMgdGhlIFB5dGhvbiBpbXBsZW1lbnRhdGlvbjoNCg0KIyMjIyAqKjEuIFNpZ21vaWQgRnVuY3Rpb24qKg0KYGBgcHl0aG9uDQppbXBvcnQgbnVtcHkgYXMgbnANCg0KZGVmIHNpZ21vaWQoeik6DQogICAgIiIiQ29tcHV0ZSBzaWdtb2lkIGZ1bmN0aW9uLiIiIg0KICAgIHJldHVybiAxIC8gKDEgKyBucC5leHAoLXopKQ0KYGBgDQoNCi0tLQ0KDQojIyMjICoqMi4gTG9nIExvc3MgRnVuY3Rpb24qKg0KYGBgcHl0aG9uDQpkZWYgbG9nX2xvc3MoeV90cnVlLCB5X3ByZWQpOg0KICAgICIiIg0KICAgIENvbXB1dGUgYmluYXJ5IGxvZyBsb3NzLg0KICAgIA0KICAgIFBhcmFtZXRlcnM6DQogICAgLSB5X3RydWU6IFRydWUgYmluYXJ5IGxhYmVscyAoYXJyYXktbGlrZSkuDQogICAgLSB5X3ByZWQ6IFByZWRpY3RlZCBwcm9iYWJpbGl0aWVzIChhcnJheS1saWtlKS4NCiAgICANCiAgICBSZXR1cm5zOg0KICAgIC0gQXZlcmFnZSBsb2cgbG9zcy4NCiAgICAiIiINCiAgICBlcHNpbG9uID0gMWUtMTUgICMgVG8gYXZvaWQgbG9nKDApDQogICAgeV9wcmVkID0gbnAuY2xpcCh5X3ByZWQsIGVwc2lsb24sIDEgLSBlcHNpbG9uKQ0KICAgIHJldHVybiAtbnAubWVhbih5X3RydWUgKiBucC5sb2coeV9wcmVkKSArICgxIC0geV90cnVlKSAqIG5wLmxvZygxIC0geV9wcmVkKSkNCmBgYA0KDQotLS0NCg0KIyMjIyAqKjMuIExvZ2lzdGljIFJlZ3Jlc3Npb24gVXNpbmcgR3JhZGllbnQgRGVzY2VudCoqDQpgYGBweXRob24NCmRlZiBsb2dpc3RpY19yZWdyZXNzaW9uKFgsIHksIGxyPTAuMDEsIGVwb2Nocz0xMDAwKToNCiAgICAiIiINCiAgICBUcmFpbiBsb2dpc3RpYyByZWdyZXNzaW9uIHVzaW5nIGdyYWRpZW50IGRlc2NlbnQuDQogICAgDQogICAgUGFyYW1ldGVyczoNCiAgICAtIFg6IEZlYXR1cmUgbWF0cml4IChOIHggZCkuDQogICAgLSB5OiBMYWJlbHMgKE4geCAxKS4NCiAgICAtIGxyOiBMZWFybmluZyByYXRlLg0KICAgIC0gZXBvY2hzOiBOdW1iZXIgb2YgaXRlcmF0aW9ucy4NCiAgICANCiAgICBSZXR1cm5zOg0KICAgIC0gd2VpZ2h0czogVHJhaW5lZCB3ZWlnaHRzLg0KICAgIC0gbG9zc2VzOiBMb2cgbG9zcyBwZXIgZXBvY2guDQogICAgIiIiDQogICAgTiwgZCA9IFguc2hhcGUNCiAgICB3ZWlnaHRzID0gbnAuemVyb3MoZCkgICMgSW5pdGlhbGl6ZSB3ZWlnaHRzDQogICAgbG9zc2VzID0gW10NCg0KICAgIGZvciBlcG9jaCBpbiByYW5nZShlcG9jaHMpOg0KICAgICAgICAjIExpbmVhciBjb21iaW5hdGlvbg0KICAgICAgICB6ID0gbnAuZG90KFgsIHdlaWdodHMpDQogICAgICAgICMgUHJlZGljdGVkIHByb2JhYmlsaXRpZXMNCiAgICAgICAgcHJlZHMgPSBzaWdtb2lkKHopDQogICAgICAgICMgQ29tcHV0ZSBsb2cgbG9zcw0KICAgICAgICBsb3NzID0gbG9nX2xvc3MoeSwgcHJlZHMpDQogICAgICAgIGxvc3Nlcy5hcHBlbmQobG9zcykNCiAgICAgICAgIyBHcmFkaWVudCBjYWxjdWxhdGlvbg0KICAgICAgICBncmFkaWVudCA9IG5wLmRvdChYLlQsIChwcmVkcyAtIHkpKSAvIE4NCiAgICAgICAgIyBXZWlnaHQgdXBkYXRlDQogICAgICAgIHdlaWdodHMgLT0gbHIgKiBncmFkaWVudA0KDQogICAgcmV0dXJuIHdlaWdodHMsIGxvc3Nlcw0KYGBgDQoNCi0tLQ0KDQojIyMjICoqNC4gVmlzdWFsaXphdGlvbiBvZiBMb2cgTG9zcyBNaW5pbWl6YXRpb24qKg0KYGBgcHl0aG9uDQppbXBvcnQgbWF0cGxvdGxpYi5weXBsb3QgYXMgcGx0DQoNCiMgR2VuZXJhdGUgc3ludGhldGljIGRhdGENCm5wLnJhbmRvbS5zZWVkKDQyKQ0KWCA9IG5wLnJhbmRvbS5yYW5kKDEwMCwgMikNCnkgPSAoWFs6LCAwXSArIFhbOiwgMV0gPiAxKS5hc3R5cGUoaW50KQ0KDQojIFRyYWluIGxvZ2lzdGljIHJlZ3Jlc3Npb24NCndlaWdodHMsIGxvc3NlcyA9IGxvZ2lzdGljX3JlZ3Jlc3Npb24oWCwgeSwgbHI9MC4xLCBlcG9jaHM9MjAwKQ0KDQojIFBsb3QgbG9nIGxvc3MNCnBsdC5wbG90KGxvc3NlcykNCnBsdC50aXRsZSgiTG9nIExvc3MgTWluaW1pemF0aW9uIikNCnBsdC54bGFiZWwoIkVwb2NocyIpDQpwbHQueWxhYmVsKCJMb2cgTG9zcyIpDQpwbHQuZ3JpZCgpDQpwbHQuc2hvdygpDQpgYGANCg0KLS0tDQoNCiMjIyAqKktleSBJbnNpZ2h0cyoqDQoxLiAqKkNvbnZleGl0eSBvZiBMb2cgTG9zcyoqOg0KICAgLSBMb2cgbG9zcyBpcyBjb252ZXgsIGVuc3VyaW5nIGEgZ2xvYmFsIG1pbmltdW0uDQogICAtIEdyYWRpZW50IGRlc2NlbnQgaXMgZ3VhcmFudGVlZCB0byBjb252ZXJnZSB3aXRoIGEgcHJvcGVyIGxlYXJuaW5nIHJhdGUuDQoNCjIuICoqV2VpZ2h0IFVwZGF0ZXMgYW5kIEVycm9yKio6DQogICAtIEVhY2ggd2VpZ2h0IHVwZGF0ZSByZWR1Y2VzIHRoZSBwcmVkaWN0aW9uIGVycm9yIGJ5IG1vdmluZyBpbiB0aGUgZGlyZWN0aW9uIG9wcG9zaXRlIHRvIHRoZSBncmFkaWVudC4NCg0KMy4gKipSZWxhdGlvbnNoaXAgdG8gTGluZWFyIFJlZ3Jlc3Npb24qKjoNCiAgIC0gTG9naXN0aWMgcmVncmVzc2lvbiBleHRlbmRzIGxpbmVhciByZWdyZXNzaW9uIGJ5IHNxdWFzaGluZyBwcmVkaWN0aW9ucyBpbnRvIHByb2JhYmlsaXRpZXMgdXNpbmcgdGhlIHNpZ21vaWQgZnVuY3Rpb24uDQogICAtIFRoZSBncmFkaWVudCB1cGRhdGUgcnVsZSBpcyBzdHJ1Y3R1cmFsbHkgc2ltaWxhciwgZGlmZmVyaW5nIG1haW5seSBpbiB0aGUgc2lnbW9pZCBub25saW5lYXJpdHkuDQoNCi0tLQ0KDQpUaGlzIG1hdGhlbWF0aWNhbCBhbmQgY29kaW5nIGJyZWFrZG93biBoaWdobGlnaHRzIHRoZSBpbnR1aXRpdmUgYW5kIHByYWN0aWNhbCBhc3BlY3RzIG9mIG1pbmltaXppbmcgbG9nIGxvc3MsIG1ha2luZyBsb2dpc3RpYyByZWdyZXNzaW9uIGEgcm9idXN0IHRvb2wgZm9yIGJpbmFyeSBjbGFzc2lmaWNhdGlvbiB0YXNrcy4NCg0KIyMjIFN1bW1hcnkgYW5kIEV4cGFuc2lvbjogTWluaW1pemluZyBMb2cgTG9zcyBpbiBMb2dpc3RpYyBSZWdyZXNzaW9uDQoNClRoaXMgZXhwbGFuYXRpb24gZm9jdXNlcyBvbiBob3cgdG8gbWluaW1pemUgdGhlICoqbG9nIGxvc3MgZnVuY3Rpb24qKiBpbiBsb2dpc3RpYyByZWdyZXNzaW9uLCB3aGljaCBpcyBlc3NlbnRpYWwgZm9yIHRyYWluaW5nIHRoZSBtb2RlbCB0byBtYWtlIGFjY3VyYXRlIHByZWRpY3Rpb25zLiBUaGUgcHJvY2VzcyBpbnZvbHZlcyBsZXZlcmFnaW5nIG9wdGltaXphdGlvbiB0ZWNobmlxdWVzLCBwYXJ0aWN1bGFybHkgZ3JhZGllbnQgZGVzY2VudCwgdG8gYWRqdXN0IHRoZSBtb2RlbCdzIHBhcmFtZXRlcnMgKHNsb3BlcykgYW5kIGFjaGlldmUgYSB3ZWxsLWZpdCBtb2RlbC4NCg0KLS0tDQoNCiMjIyAqKjEuIE9iamVjdGl2ZSBvZiBNaW5pbWl6aW5nIExvZyBMb3NzKioNCi0gKipHb2FsKio6IE1pbmltaXppbmcgbG9nIGxvc3MgZW5zdXJlcyB0aGF0IHRoZSBtb2RlbCBwcm9kdWNlcyBwcmVkaWN0aW9ucyB0aGF0IGFyZSBhcyBjbG9zZSBhcyBwb3NzaWJsZSB0byB0aGUgYWN0dWFsIHRhcmdldCB2YWx1ZXMuDQotICoqU21hbGwgTG9zcyA9IEdvb2QgTW9kZWwqKjogQSBzbWFsbGVyIGxvZyBsb3NzIGluZGljYXRlcyBhIHdlbGwtdHJhaW5lZCBtb2RlbCB3aXRoIGFjY3VyYXRlIHByZWRpY3Rpb25zLg0KDQotLS0NCg0KIyMjICoqMi4gTG9nIExvc3MgRnVuY3Rpb24gUmVjYXAqKg0KLSBUaGUgbG9nIGxvc3MgZnVuY3Rpb24gKFwoSlwpKSBpcyBkZWZpbmVkIGFzOg0KICBcWw0KICBKID0gLSBcZnJhY3sxfXtufSBcc3VtX3tpPTF9Xm4gXGxlZnRbIHlfaSBcY2RvdCBcbG9nKHBfaSkgKyAoMSAtIHlfaSkgXGNkb3QgXGxvZygxIC0gcF9pKSBccmlnaHRdDQogIFxdDQogIC0gXCh5X2lcKTogQWN0dWFsIHRhcmdldCAoMCBvciAxKS4NCiAgLSBcKHBfaVwpOiBQcmVkaWN0ZWQgcHJvYmFiaWxpdHkgKG91dHB1dCBvZiB0aGUgc2lnbW9pZCBmdW5jdGlvbikuDQogIC0gXChuXCk6IE51bWJlciBvZiBkYXRhIHBvaW50cy4NCg0KLSAqKktleSBJbnNpZ2h0Kio6DQogIC0gXChwXCkgaXMgbm90IGp1c3QgYSBudW1iZXI7IGl0IGlzIHRoZSAqKm91dHB1dCBvZiB0aGUgc2lnbW9pZCBmdW5jdGlvbioqOg0KICAgIFxbDQogICAgcCA9IFxzaWdtYShtIFxjZG90IHgpID0gXGZyYWN7MX17MSArIGVeey1tIFxjZG90IHh9fQ0KICAgIFxdDQogIC0gVGhlIHNpZ21vaWQgZnVuY3Rpb24gZGVwZW5kcyBvbiB0aGUgaW5wdXQgZGF0YSAoXCh4XCkpIGFuZCB0aGUgbW9kZWwgcGFyYW1ldGVycyAoXChtXCksIHRoZSBzbG9wZXMpLg0KDQotLS0NCg0KIyMjICoqMy4gTWluaW1pemluZyBMb2cgTG9zcyoqDQotIFRvIG1pbmltaXplIGxvZyBsb3NzLCB0aGUgcHJvY2VzcyBpbnZvbHZlczoNCiAgMS4gKipGaXhpbmcgdGhlIERhdGEqKjoNCiAgICAgLSBUaGUgdGFyZ2V0IHZhbHVlcyAoXCh5XCkpIGFuZCBpbnB1dCBkYXRhIChcKHhcKSkgYXJlIGZpeGVkIGFuZCBjYW5ub3QgYmUgY2hhbmdlZC4NCiAgMi4gKipBZGp1c3RpbmcgdGhlIFNsb3BlcyoqOg0KICAgICAtIFRoZSBzbG9wZXMgKFwobVwpKSBhcmUgdGhlIG9ubHkgdmFyaWFibGVzIHRoYXQgY2FuIGJlIHVwZGF0ZWQgdG8gbWluaW1pemUgXChKXCkuDQoNCi0tLQ0KDQojIyMgKio0LiBHcmFkaWVudCBEZXNjZW50IGZvciBPcHRpbWl6YXRpb24qKg0KLSAqKkdyYWRpZW50IERlc2NlbnQqKjoNCiAgLSBHcmFkaWVudCBkZXNjZW50IGlzIHVzZWQgdG8gZmluZCB0aGUgbWluaW11bSBvZiB0aGUgbG9nIGxvc3MgZnVuY3Rpb24uDQogIC0gVGhlIHBhcnRpYWwgZGVyaXZhdGl2ZSBvZiBcKEpcKSB3aXRoIHJlc3BlY3QgdG8gZWFjaCBzbG9wZSAoXChtX2lcKSkgZGV0ZXJtaW5lcyB0aGUgZGlyZWN0aW9uIGFuZCBtYWduaXR1ZGUgb2YgdGhlIHVwZGF0ZS4NCiAgLSBUaGUgdXBkYXRlIHJ1bGUgaXM6DQogICAgXFsNCiAgICBtX2kgPSBtX2kgLSBcYWxwaGEgXGNkb3QgXGZyYWN7XHBhcnRpYWwgSn17XHBhcnRpYWwgbV9pfQ0KICAgIFxdDQogICAgLSBcKFxhbHBoYVwpOiBMZWFybmluZyByYXRlIChjb250cm9scyB0aGUgc3RlcCBzaXplIG9mIHRoZSB1cGRhdGUpLg0KICAgIC0gXChcZnJhY3tccGFydGlhbCBKfXtccGFydGlhbCBtX2l9XCk6IFBhcnRpYWwgZGVyaXZhdGl2ZSBvZiBcKEpcKSB3aXRoIHJlc3BlY3QgdG8gXChtX2lcKS4NCg0KLSAqKktleSBJbnNpZ2h0Kio6DQogIC0gVGhlIGRlcml2YXRpdmUgb2YgdGhlIGxvZyBsb3NzIGZ1bmN0aW9uIHdpdGggdGhlIHNpZ21vaWQgZnVuY3Rpb24gaXMgbWF0aGVtYXRpY2FsbHkgc3RydWN0dXJlZCBzdWNoIHRoYXQgdGhlIHVwZGF0ZSBydWxlIGlzICoqaWRlbnRpY2FsKiogdG8gdGhhdCBvZiBsaW5lYXIgcmVncmVzc2lvbi4gVGhpcyBpcyBkdWUgdG8gdGhlIGNob2ljZSBvZiB0aGUgc2lnbW9pZCBhbmQgbG9nIGZ1bmN0aW9ucy4NCg0KLS0tDQoNCiMjIyAqKjUuIFdoeSB0aGUgU2lnbW9pZCBhbmQgTG9nIEZ1bmN0aW9ucz8qKg0KLSBUaGUgc2lnbW9pZCBhbmQgbG9nIGZ1bmN0aW9ucyB3ZXJlIHNwZWNpZmljYWxseSBjaG9zZW4gYmVjYXVzZToNCiAgLSBUaGUgbG9nIGxvc3MgZnVuY3Rpb24gaXMgKipjb252ZXgqKiwgZW5zdXJpbmcgYSBzaW5nbGUgZ2xvYmFsIG1pbmltdW0uDQogIC0gVGhlIGRlcml2YXRpdmUgb2YgdGhlIG5hdHVyYWwgbG9nIChcKFxsb2dfZVwpKSBpcyBzaW1wbGU6DQogICAgXFsNCiAgICBcZnJhY3tkfXtkcH0gXGxvZyhwKSA9IFxmcmFjezF9e3B9DQogICAgXF0NCiAgLSBUaGlzIHNpbXBsaWNpdHkgZW5zdXJlcyB0aGF0IHRoZSB1cGRhdGUgcnVsZXMgZm9yIGxvZ2lzdGljIHJlZ3Jlc3Npb24gYWxpZ24gd2l0aCB0aG9zZSBmb3IgbGluZWFyIHJlZ3Jlc3Npb24sIGFsbG93aW5nIHRoZSBzYW1lIG9wdGltaXphdGlvbiBhbGdvcml0aG1zIHRvIGJlIHJldXNlZC4NCg0KLS0tDQoNCiMjIyAqKjYuIFByYWN0aWNhbCBJbXBsaWNhdGlvbnMqKg0KLSAqKlVuaWZpZWQgT3B0aW1pemF0aW9uKio6DQogIC0gVGhlIGZhY3QgdGhhdCBsb2dpc3RpYyByZWdyZXNzaW9uIHNoYXJlcyB0aGUgc2FtZSB1cGRhdGUgcnVsZSBhcyBsaW5lYXIgcmVncmVzc2lvbiBzaW1wbGlmaWVzIGltcGxlbWVudGF0aW9uLg0KICAtIEFsZ29yaXRobXMgbGlrZSBzdG9jaGFzdGljIGdyYWRpZW50IGRlc2NlbnQgKFNHRCkgb3IgYmF0Y2ggZ3JhZGllbnQgZGVzY2VudCBjYW4gYmUgZGlyZWN0bHkgYXBwbGllZC4NCi0gKipFeHRlbmRpbmcgTGluZWFyIFJlZ3Jlc3Npb24qKjoNCiAgLSBCeSBzaW1wbHkgY2hhbmdpbmcgdGhlIGxvc3MgZnVuY3Rpb24gKGZyb20gbWVhbiBzcXVhcmVkIGVycm9yIHRvIGxvZyBsb3NzKSwgbG9naXN0aWMgcmVncmVzc2lvbiBleHRlbmRzIGxpbmVhciByZWdyZXNzaW9uIHRvIGhhbmRsZSBjYXRlZ29yaWNhbCBwcm9ibGVtcy4NCg0KLS0tDQoNCiMjIyAqKjcuIEtleSBUYWtlYXdheXMqKg0KLSAqKk1pbmltaXppbmcgTG9nIExvc3MqKjoNCiAgLSBBY2hpZXZlZCBieSBhZGp1c3RpbmcgdGhlIHNsb3BlcyAoXChtXCkpIHVzaW5nIGdyYWRpZW50IGRlc2NlbnQuDQogIC0gVGhlIGNvbnZleGl0eSBvZiB0aGUgbG9nIGxvc3MgZnVuY3Rpb24gZW5zdXJlcyBhbiBlYXN5LXRvLWZpbmQgZ2xvYmFsIG1pbmltdW0uDQotICoqUmV1c2FiaWxpdHkqKjoNCiAgLSBUaGUgZGVyaXZhdGl2ZSBvZiB0aGUgbG9nIGxvc3MgZnVuY3Rpb24gYWxpZ25zIHdpdGggbGluZWFyIHJlZ3Jlc3Npb24ncyB1cGRhdGUgcnVsZXMsIG1ha2luZyBvcHRpbWl6YXRpb24gc3RyYWlnaHRmb3J3YXJkLg0KLSAqKldoeSB0aGUgU2lnbW9pZCBGdW5jdGlvbj8qKjoNCiAgLSBUaGUgc2lnbW9pZCBmdW5jdGlvbiBlbnN1cmVzIG91dHB1dHMgYXJlIHByb2JhYmlsaXRpZXMgKGJldHdlZW4gMCBhbmQgMSkuDQogIC0gSXRzIGV4cG9uZW50aWFsIHN0cnVjdHVyZSBzaW1wbGlmaWVzIGRlcml2YXRpdmVzLCB3aGljaCBpcyBjcnVjaWFsIGZvciBlZmZpY2llbnQgb3B0aW1pemF0aW9uLg0KDQotLS0NCg0KIyMjICoqQ29uY2x1c2lvbioqDQpNaW5pbWl6aW5nIGxvZyBsb3NzIGluIGxvZ2lzdGljIHJlZ3Jlc3Npb24gaXMgYSBzdHJhaWdodGZvcndhcmQgZXh0ZW5zaW9uIG9mIGxpbmVhciByZWdyZXNzaW9uJ3Mgb3B0aW1pemF0aW9uIHByb2Nlc3MuIEJ5IHVzaW5nIHRoZSBzaWdtb2lkIGZ1bmN0aW9uIGFuZCBsb2cgbG9zcywgdGhlIG1vZGVsIGNhbiBoYW5kbGUgY2F0ZWdvcmljYWwgZGF0YSBlZmZlY3RpdmVseSB3aGlsZSBtYWludGFpbmluZyB0aGUgc2FtZSBvcHRpbWl6YXRpb24gZnJhbWV3b3JrLiBUaGlzIG1hdGhlbWF0aWNhbCBkZXNpZ24gZW5zdXJlcyBib3RoIHNpbXBsaWNpdHkgYW5kIGVmZmljaWVuY3kgaW4gdHJhaW5pbmcgbG9naXN0aWMgcmVncmVzc2lvbiBtb2RlbHMuDQoNCiMjIyAqKk11bHRpY2xhc3MgQ2xhc3NpZmljYXRpb246IE92ZXJ2aWV3IGFuZCBFeHBsYW5hdGlvbioqDQoNCk11bHRpY2xhc3MgY2xhc3NpZmljYXRpb24gZXh0ZW5kcyBiaW5hcnkgY2xhc3NpZmljYXRpb24gdGVjaG5pcXVlcyB0byBwcm9ibGVtcyB3aGVyZSB0aGVyZSBhcmUgbW9yZSB0aGFuIHR3byBjbGFzc2VzLiBGb3IgaW5zdGFuY2UsIGluc3RlYWQgb2YgZGV0ZXJtaW5pbmcgaWYgYW4gaW5wdXQgYmVsb25ncyB0byAiY2F0IiBvciAiZG9nLCIgd2UgbWlnaHQgY2xhc3NpZnkgYmV0d2VlbiAiY2F0LCIgImRvZywiIGFuZCAicmFiYml0LiINCg0KLS0tDQoNCiMjIyAqKktleSBBcHByb2FjaGVzIHRvIE11bHRpY2xhc3MgQ2xhc3NpZmljYXRpb24qKg0KDQojIyMjICoqMS4gT25lLXZzLVJlc3QgKE92UikgTWV0aG9kKioNCi0gKipDb25jZXB0Kio6IFRyYWluIHNlcGFyYXRlIGJpbmFyeSBjbGFzc2lmaWVycyBmb3IgZWFjaCBjbGFzcy4NCiAgLSBGb3IgXCggSyBcKSBjbGFzc2VzLCB3ZSB0cmFpbiBcKCBLIFwpIG1vZGVsczoNCiAgICAtIFwoIFx0ZXh0e0NsYXNzfV9pIFwpIHZzLiAibm90LVwoIFx0ZXh0e0NsYXNzfV9pIFwpIg0KICAtIEV4YW1wbGU6DQogICAgLSBDbGFzc2VzOiBSZWQsIEdyZWVuLCBCbHVlLg0KICAgIC0gTW9kZWxzOiBSZWQtdnMtbm90LVJlZCwgR3JlZW4tdnMtbm90LUdyZWVuLCBCbHVlLXZzLW5vdC1CbHVlLg0KLSAqKlByZWRpY3Rpb24qKjogVXNlIHRoZSAqKmFyZ21heCoqIGZ1bmN0aW9uIHRvIGNob29zZSB0aGUgY2xhc3Mgd2l0aCB0aGUgaGlnaGVzdCBwcm9iYWJpbGl0eToNCiAgXFsNCiAgXGhhdHt5fSA9IFx0ZXh0e2FyZ21heH1fayBcOyBcdGV4dHtzaWdtb2lkfShcbWF0aGJme3d9X2teVCBcbWF0aGJme3h9KQ0KICBcXQ0KLSAqKlByb3MqKjoNCiAgLSBTaW1wbGUgYW5kIGVhc3kgdG8gaW1wbGVtZW50Lg0KICAtIFdvcmtzIHdlbGwgZm9yIGEgc21hbGwgbnVtYmVyIG9mIGNsYXNzZXMuDQotICoqQ29ucyoqOg0KICAtIEJpYXMgdG93YXJkcyB0aGUgbmVnYXRpdmUgY2xhc3Mgd2hlbiBjbGFzcyBkaXN0cmlidXRpb25zIGFyZSBpbWJhbGFuY2VkLg0KICAtIFNjYWxlcyBwb29ybHkgd2l0aCB0aGUgbnVtYmVyIG9mIGNsYXNzZXMuDQoNCi0tLQ0KDQojIyMjICoqMi4gT25lLXZzLU9uZSAoT3ZPKSBNZXRob2QqKg0KLSAqKkNvbmNlcHQqKjogVHJhaW4gYSBiaW5hcnkgY2xhc3NpZmllciBmb3IgZWFjaCBwYWlyIG9mIGNsYXNzZXMuDQogIC0gRm9yIFwoIEsgXCkgY2xhc3NlcywgdHJhaW4gXCggXGJpbm9te0t9ezJ9ID0gXGZyYWN7SyhLLTEpfXsyfSBcKSBtb2RlbHMuDQogICAgLSBFYWNoIG1vZGVsIGRpc3Rpbmd1aXNoZXMgYmV0d2VlbiB0d28gY2xhc3NlcywgZS5nLiwgXCggXHRleHR7Q2xhc3N9X2kgXCkgdnMuIFwoIFx0ZXh0e0NsYXNzfV9qIFwpLg0KICAtIEV4YW1wbGU6DQogICAgLSBDbGFzc2VzOiBSZWQsIEdyZWVuLCBCbHVlLg0KICAgIC0gTW9kZWxzOiBSZWQtdnMtR3JlZW4sIFJlZC12cy1CbHVlLCBHcmVlbi12cy1CbHVlLg0KLSAqKlByZWRpY3Rpb24qKjogVXNlIG1ham9yaXR5IHZvdGluZzoNCiAgLSBFYWNoIGNsYXNzaWZpZXIgInZvdGVzIiBmb3Igb25lIGNsYXNzLg0KICAtIFRoZSBjbGFzcyB3aXRoIHRoZSBtb3N0IHZvdGVzIHdpbnMuDQotICoqUHJvcyoqOg0KICAtIEhhbmRsZXMgaW1iYWxhbmNlZCBjbGFzc2VzIGJldHRlciB0aGFuIE92Ui4NCiAgLSBPZnRlbiBwZXJmb3JtcyB3ZWxsIGZvciBzbWFsbCBcKCBLIFwpLg0KLSAqKkNvbnMqKjoNCiAgLSBDb21wdXRhdGlvbmFsbHkgZXhwZW5zaXZlIGZvciBsYXJnZSBcKCBLIFwpIGR1ZSB0byB0aGUgXCggTyhLXjIpIFwpIG51bWJlciBvZiBtb2RlbHMuDQogIC0gUG90ZW50aWFsIGZvciB0aWVzIG9yIHBhcmFkb3hpY2FsIHJlc3VsdHMgaW4gdm90aW5nLg0KDQotLS0NCg0KIyMjICoqTWF0aGVtYXRpY2FsIFJlcHJlc2VudGF0aW9uKioNCg0KIyMjIyAqKkxvZyBMb3NzIGZvciBNdWx0aWNsYXNzIFByb2JsZW1zKioNCkZvciBhIGRhdGFzZXQgd2l0aCBcKCBLIFwpIGNsYXNzZXM6DQoxLiAqKlNvZnRtYXggQWN0aXZhdGlvbioqOg0KICAgLSBHZW5lcmFsaXplcyB0aGUgc2lnbW9pZCBmdW5jdGlvbiBmb3IgbXVsdGljbGFzczoNCiAgICAgXFsNCiAgICAgcCh5PWt8XG1hdGhiZnt4fSkgPSBcZnJhY3tlXntcbWF0aGJme3d9X2teVCBcbWF0aGJme3h9fX17XHN1bV97aj0xfV5LIGVee1xtYXRoYmZ7d31fal5UIFxtYXRoYmZ7eH19fQ0KICAgICBcXQ0KICAgLSBPdXRwdXRzIGEgcHJvYmFiaWxpdHkgZGlzdHJpYnV0aW9uIG92ZXIgXCggSyBcKSBjbGFzc2VzLg0KDQoyLiAqKkNyb3NzLUVudHJvcHkgTG9zcyoqOg0KICAgLSBNZWFzdXJlcyB0aGUgZGlmZmVyZW5jZSBiZXR3ZWVuIHByZWRpY3RlZCBhbmQgdHJ1ZSBwcm9iYWJpbGl0eSBkaXN0cmlidXRpb25zOg0KICAgICBcWw0KICAgICBKKFxtYXRoYmZ7V30pID0gLVxmcmFjezF9e059IFxzdW1fe2k9MX1eTiBcc3VtX3trPTF9XksgeV97aSxrfSBcbG9nKHAoeT1rfFxtYXRoYmZ7eH1faSkpDQogICAgIFxdDQogICAtIFwoIFxtYXRoYmZ7V30gPSBbXG1hdGhiZnt3fV8xLCBcbWF0aGJme3d9XzIsIFxsZG90cywgXG1hdGhiZnt3fV9LXSBcKTogd2VpZ2h0IG1hdHJpeCBmb3IgXCggSyBcKSBjbGFzc2VzLg0KICAgLSBcKCB5X3tpLGt9IFwpOiBPbmUtaG90IGVuY29kZWQgdHJ1ZSBsYWJlbCBmb3IgXCggXG1hdGhiZnt4fV9pIFwpLg0KDQotLS0NCg0KIyMjICoqUHl0aG9uIEltcGxlbWVudGF0aW9uKioNCg0KIyMjIyAqKlNvZnRtYXggRnVuY3Rpb24qKg0KYGBgcHl0aG9uDQpkZWYgc29mdG1heCh6KToNCiAgICAiIiINCiAgICBDb21wdXRlIHRoZSBzb2Z0bWF4IG9mIHZlY3RvciB6Lg0KICAgIA0KICAgIFBhcmFtZXRlcnM6DQogICAgLSB6OiAyRCBhcnJheSBvZiBzaGFwZSAoTiwgSyksIHdoZXJlIE4gaXMgdGhlIG51bWJlciBvZiBzYW1wbGVzIGFuZCBLIGlzIHRoZSBudW1iZXIgb2YgY2xhc3Nlcy4NCiAgICANCiAgICBSZXR1cm5zOg0KICAgIC0gU29mdG1heCBwcm9iYWJpbGl0aWVzIGZvciBlYWNoIGNsYXNzLg0KICAgICIiIg0KICAgIGV4cF96ID0gbnAuZXhwKHogLSBucC5tYXgoeiwgYXhpcz0xLCBrZWVwZGltcz1UcnVlKSkgICMgU3RhYmlsaXplIGZvciBudW1lcmljYWwgc2FmZXR5DQogICAgcmV0dXJuIGV4cF96IC8gbnAuc3VtKGV4cF96LCBheGlzPTEsIGtlZXBkaW1zPVRydWUpDQpgYGANCg0KLS0tDQoNCiMjIyMgKipDcm9zcy1FbnRyb3B5IExvc3MqKg0KYGBgcHl0aG9uDQpkZWYgY3Jvc3NfZW50cm9weV9sb3NzKHlfdHJ1ZSwgeV9wcmVkKToNCiAgICAiIiINCiAgICBDb21wdXRlIHRoZSBjcm9zcy1lbnRyb3B5IGxvc3MgZm9yIG11bHRpY2xhc3MgY2xhc3NpZmljYXRpb24uDQogICAgDQogICAgUGFyYW1ldGVyczoNCiAgICAtIHlfdHJ1ZTogT25lLWhvdCBlbmNvZGVkIHRydWUgbGFiZWxzIG9mIHNoYXBlIChOLCBLKS4NCiAgICAtIHlfcHJlZDogUHJlZGljdGVkIHByb2JhYmlsaXRpZXMgb2Ygc2hhcGUgKE4sIEspLg0KICAgIA0KICAgIFJldHVybnM6DQogICAgLSBBdmVyYWdlIGNyb3NzLWVudHJvcHkgbG9zcy4NCiAgICAiIiINCiAgICBlcHNpbG9uID0gMWUtMTUgICMgQXZvaWQgbG9nKDApDQogICAgeV9wcmVkID0gbnAuY2xpcCh5X3ByZWQsIGVwc2lsb24sIDEgLSBlcHNpbG9uKQ0KICAgIHJldHVybiAtbnAubWVhbihucC5zdW0oeV90cnVlICogbnAubG9nKHlfcHJlZCksIGF4aXM9MSkpDQpgYGANCg0KLS0tDQoNCiMjIyMgKipNdWx0aWNsYXNzIExvZ2lzdGljIFJlZ3Jlc3Npb24gVHJhaW5pbmcqKg0KYGBgcHl0aG9uDQpkZWYgdHJhaW5fbXVsdGljbGFzc19sb2dpc3RpY19yZWdyZXNzaW9uKFgsIHksIGxyPTAuMDEsIGVwb2Nocz0xMDAwKToNCiAgICAiIiINCiAgICBUcmFpbiBhIG11bHRpY2xhc3MgbG9naXN0aWMgcmVncmVzc2lvbiBtb2RlbCB1c2luZyBncmFkaWVudCBkZXNjZW50Lg0KICAgIA0KICAgIFBhcmFtZXRlcnM6DQogICAgLSBYOiBGZWF0dXJlIG1hdHJpeCAoTiB4IGQpLg0KICAgIC0geTogT25lLWhvdCBlbmNvZGVkIGxhYmVscyAoTiB4IEspLg0KICAgIC0gbHI6IExlYXJuaW5nIHJhdGUuDQogICAgLSBlcG9jaHM6IE51bWJlciBvZiBpdGVyYXRpb25zLg0KICAgIA0KICAgIFJldHVybnM6DQogICAgLSBXOiBUcmFpbmVkIHdlaWdodCBtYXRyaXggKGQgeCBLKS4NCiAgICAtIGxvc3NlczogTGlzdCBvZiBjcm9zcy1lbnRyb3B5IGxvc3NlcyBwZXIgZXBvY2guDQogICAgIiIiDQogICAgTiwgZCA9IFguc2hhcGUNCiAgICBLID0geS5zaGFwZVsxXQ0KICAgIFcgPSBucC56ZXJvcygoZCwgSykpICAjIEluaXRpYWxpemUgd2VpZ2h0cw0KICAgIGxvc3NlcyA9IFtdDQoNCiAgICBmb3IgZXBvY2ggaW4gcmFuZ2UoZXBvY2hzKToNCiAgICAgICAgIyBMaW5lYXIgY29tYmluYXRpb24NCiAgICAgICAgeiA9IG5wLmRvdChYLCBXKQ0KICAgICAgICAjIFNvZnRtYXggcHJvYmFiaWxpdGllcw0KICAgICAgICBwcm9icyA9IHNvZnRtYXgoeikNCiAgICAgICAgIyBDb21wdXRlIGxvc3MNCiAgICAgICAgbG9zcyA9IGNyb3NzX2VudHJvcHlfbG9zcyh5LCBwcm9icykNCiAgICAgICAgbG9zc2VzLmFwcGVuZChsb3NzKQ0KICAgICAgICAjIEdyYWRpZW50IGNhbGN1bGF0aW9uDQogICAgICAgIGdyYWRpZW50ID0gbnAuZG90KFguVCwgKHByb2JzIC0geSkpIC8gTg0KICAgICAgICAjIFdlaWdodCB1cGRhdGUNCiAgICAgICAgVyAtPSBsciAqIGdyYWRpZW50DQoNCiAgICByZXR1cm4gVywgbG9zc2VzDQpgYGANCg0KLS0tDQoNCiMjIyMgKipWaXN1YWxpemF0aW9uIG9mIExvc3MgTWluaW1pemF0aW9uKioNCmBgYHB5dGhvbg0KaW1wb3J0IG1hdHBsb3RsaWIucHlwbG90IGFzIHBsdA0KDQojIEdlbmVyYXRlIHN5bnRoZXRpYyBkYXRhDQpucC5yYW5kb20uc2VlZCg0MikNClggPSBucC5yYW5kb20ucmFuZCgxMDAsIDMpDQp5X3JhdyA9IG5wLnJhbmRvbS5yYW5kaW50KDAsIDMsIDEwMCkgICMgUmFuZG9tIGxhYmVscyAoMCwgMSwgMikNCnlfb25lX2hvdCA9IG5wLmV5ZSgzKVt5X3Jhd10gICMgT25lLWhvdCBlbmNvZGUgbGFiZWxzDQoNCiMgVHJhaW4gbG9naXN0aWMgcmVncmVzc2lvbg0KVywgbG9zc2VzID0gdHJhaW5fbXVsdGljbGFzc19sb2dpc3RpY19yZWdyZXNzaW9uKFgsIHlfb25lX2hvdCwgbHI9MC4xLCBlcG9jaHM9MzAwKQ0KDQojIFBsb3QgbG9zcw0KcGx0LnBsb3QobG9zc2VzKQ0KcGx0LnRpdGxlKCJDcm9zcy1FbnRyb3B5IExvc3MgTWluaW1pemF0aW9uIikNCnBsdC54bGFiZWwoIkVwb2NocyIpDQpwbHQueWxhYmVsKCJMb3NzIikNCnBsdC5ncmlkKCkNCnBsdC5zaG93KCkNCmBgYA0KDQotLS0NCg0KIyMjICoqS2V5IEluc2lnaHRzKioNCjEuICoqU29mdG1heCBmb3IgTXVsdGljbGFzcyoqOg0KICAgLSBTb2Z0bWF4IGVuc3VyZXMgdGhlIHN1bSBvZiBwcmVkaWN0ZWQgcHJvYmFiaWxpdGllcyBlcXVhbHMgMSwgc3VpdGFibGUgZm9yIG11bHRpY2xhc3MgcHJvYmxlbXMuDQoyLiAqKk9uZS12cy1SZXN0Kio6DQogICAtIFNpbXBsZSB0byBpbXBsZW1lbnQsIGJ1dCBzY2FsZXMgcG9vcmx5IGZvciBsYXJnZSBcKCBLIFwpLg0KMy4gKipPbmUtdnMtT25lKio6DQogICAtIENvbXB1dGF0aW9uYWxseSBleHBlbnNpdmUgYnV0IHVzZWZ1bCBmb3Igc21hbGwgXCggSyBcKS4NCjQuICoqQ3Jvc3MtRW50cm9weSBMb3NzKio6DQogICAtIFRoZSBsb3NzIGZ1bmN0aW9uIGdlbmVyYWxpemVzIGJpbmFyeSBjcm9zcy1lbnRyb3B5IGZvciBtdWx0aWNsYXNzIHByb2JsZW1zLg0KNS4gKipPcHRpbWl6YXRpb24qKjoNCiAgIC0gR3JhZGllbnQgZGVzY2VudCB3b3JrcyB3ZWxsIGR1ZSB0byB0aGUgY29udmV4IG5hdHVyZSBvZiB0aGUgY3Jvc3MtZW50cm9weSBsb3NzLg0KDQpUaGlzIG1hdGhlbWF0aWNhbCBhbmQgY29kaW5nIGV4cGxhbmF0aW9uIHByb3ZpZGVzIGEgY29tcHJlaGVuc2l2ZSB1bmRlcnN0YW5kaW5nIG9mIG11bHRpY2xhc3MgY2xhc3NpZmljYXRpb24gdXNpbmcgbG9naXN0aWMgcmVncmVzc2lvbi4=