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

  1. 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.

  1. 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.

  1. 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.

  1. 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.

  1. 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.

  1. 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.

  1. 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.

  1. 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.

  1. 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.

  1. 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.

  1. 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

  1. Countable: Discrete data represents items that can be counted (e.g., the number of students in a class).
  2. 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).
  3. No Fractions or Decimals: Discrete data cannot take fractional or decimal values (e.g., you can’t have 2.5 students).
  4. 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

  1. 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.
  2. 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

Feature Discrete Data 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

  1. Visualization:
    • Use bar charts or pie charts to represent discrete data visually.
    • Example: A bar chart showing the number of students in each grade.
  2. Statistical Measures:
    • Calculate measures like mean, median, mode, and range.
    • Example: The average number of cars sold by a dealership per day.
  3. Probability:
    • Discrete data is often used in probability distributions, such as the binomial distribution or Poisson distribution.

Applications of Discrete Data


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.

  1. 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.
  2. 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.
  3. 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.
  4. 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.
  5. 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.
  6. 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.

  1. 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.
  2. 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.
  3. 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.
  4. 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.
  5. 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.
  6. 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


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

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?

This transformation creates an S-shaped curve (sigmoid curve), which is ideal for modeling probabilities.


2. Relationship to Binary Classification


3. Properties of the Sigmoid Function


4. Adjusting the Threshold


5. Why the Sigmoid Function?


6. Practical Considerations


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:

  1. 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.
  2. 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.
  3. 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\)).
  4. 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\).
  5. 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.
  6. 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

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)?


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] \]


3. Intuition Behind Log Loss

This penalization encourages the model to predict probabilities closer to the true target.


4. Why Use Log Loss?


5. Visualization of Log Loss


6. Practical Applications


7. Advantages of Log Loss


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

  1. 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\)).
  2. 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.
  3. 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.

  1. 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

  1. Convexity of Log Loss:
    • Log loss is convex, ensuring a global minimum.
    • Gradient descent is guaranteed to converge with a proper learning rate.
  2. Weight Updates and Error:
    • Each weight update reduces the prediction error by moving in the direction opposite to the gradient.
  3. 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


2. Log Loss Function Recap


3. Minimizing Log Loss


4. Gradient Descent for Optimization


5. Why the Sigmoid and Log Functions?


6. Practical Implications


7. Key Takeaways


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.

  1. 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

  1. Softmax for Multiclass:
    • Softmax ensures the sum of predicted probabilities equals 1, suitable for multiclass problems.
  2. One-vs-Rest:
    • Simple to implement, but scales poorly for large \(K\).
  3. One-vs-One:
    • Computationally expensive but useful for small \(K\).
  4. Cross-Entropy Loss:
    • The loss function generalizes binary cross-entropy for multiclass problems.
  5. 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=