Summary of the article: Interest is focused on a multi-dimensional signal \(R\) observed on a grid in the presence of both an illumination artifact and an additional additive bounded variance centered noise \(\varepsilon.\) The illumination artifact consists of colour or grey level intensity variations which are seen on the sampled image but which are not present in \(R.\) Such an assumption is classically modelled using a function \(L\) which acts multiplicatively on \(R.\) Our goal is to estimate \(R\) from observations of a random variable \(Y\) which obeys the regression model \(Y=RL+\varepsilon.\) We identify and propose a solution to an identifiability issue. We construct a consistent multi-step estimation procedure. We first make use of any consistent denoising method to estimate from the noisy data the gradient of the logarithm of the regression function. We assume that the artefact \(L\) consists of “smooth” variations. Then we project the denoised auxiliary estimate on a finite basis of “smooth” functions. We deduce the final estimator of \(R\) so that the proposed identifiability constraint is satisfied. We derive an upper bound for the sup-norm risk of our estimator. Applications to different images are presented.

Aim of this page: help you find your own way with our publicly available code!

Hardware requirement: to run our code on your data, you should be working with the latest free open source R software (www.r-project.org) or at least with R Version 3.3.1 installed on it. Moreover, your system should be run at least under windows seven or Ubuntu 10.4 or Mac OS X El Capitan 10.11.1.

If your are a beginner R user, then first go to our help page. You can skip it, if you already know how to use R.

Download our code: Download the compressed file in the prefered format and extract the archive into a new empty repertory (named as “GKV” throughout this tutorial):

Contents of the archive: Here is a schematic description of the contents of the file.
File structure

Our code is written to be used according to the following guidelines:

  1. Set R working directory
  2. Load additional packages
  3. Load your data in R workspace. Images have to be in the .png format.
  4. You can do some preliminary analysis (plot your data, plot graylevel histogram, check some pixel values, check data size)
  5. Call the main function sample-analysis.R (and modify it if needed).
  6. Plot the denoised image.
  7. Plot the illumination estimation (actually you get a level plot of the illumination).
  8. Plot the reflectance estimation (this is your processed image which is free of both noise and illumination).

Get R workspace ready

Set R working directory

Our code is written to work with “GKV” as R working directory.

# Specify the path to R working directory
workDir <- "/your-workDir-path-to/GKV"
setwd(workDir)

Load additional packages

The following packages are required: png, rasterVis. Check that these packages are installed on your system. If not, install them before loading them.

If you are working off-line, note that the rasterVis package depends on other packages that you also have to install: raster, lattice, sp, latticeExtra, RColorBrewer.

Load the technical functions

First, load the technical functions into R workspace as shown below. The script file fnct01-grayImage.R contains the code which reads, writes and plots a graylevel image. The script file fnct04-gkv-gray.R computes the illumination and the reflectance estimates. To this aim, it calls on the technical functions fnct02-gkv-utils.R and fnct03-gkv-grads.R.

# Run user-defined functions
source('./lib/fnct01-grayImage.R')
source('./lib/fnct04-gkv-gray.R')

Load and manipulate your image data in R workspace

Read, plot and write an image with default options

Let deep.png be the “.png” image we are using as an illustrated example.

To load an image into R workspace, use the function read.grayImage. The output of the function read.grayImage is a matrix containing the image pixel graylevel intensities. The intensities belong to the interval [0,1] where the value 0 stands for the black color and the value 1 stands for the white color. In our illustrated example, the matrix of pixel intensities is then stored in the object named as data.

# Run the read.grayImage function 
data <- read.grayImage('./data/deep.png')
# Run this if you want to check or need the image size
n1 <- dim(data)[1] # number of rows
n2 <- dim(data)[2] # number of columns
n1; n2

To plot this image (once it has been read), use the function plot.grayImage. By default, it is only necessary to specify the path to the image location from R working directory.

# Run the plot.grayImage function 
plot.grayImage(data)

Read, plot and write an image with user-specified options

To apply our method on this image, the image legend needs to be cut off. In fact, the region of interest of the image deep.png consists of all the pixel columns and the pixel rows from 1 to 422.

# Load data and overwrite the "data"" object
data <- read.grayImage('./data/deep.png', nrow=1:422, ncol=NULL)
# Run this if you want to check or need the size of the loaded part of the image 
n1 <- dim(data)[1] # number of rows
n2 <- dim(data)[2] # number of columns
n1; n2
# Run this if you want to plot the trimmed image
plot.grayImage(data)

More generally, a rectangular subregion of interest may be defined by the optional argument nrow= and ncol=. For instance, if the region of interest is a rectangle defined by the pixel rows from 10 to 30 and by the pixels columns from 50 to 70, then do:

# Run the read.grayImage function on a subimage
subdata <- read.grayImage('./data/deep.png', nrow=10:30, ncol=50:70)
plot.grayImage(subdata)

The object data may be written into a .png file with the function write.grayImage.

# Run this if you want to save the data matrix in the folder "cache" into a .png file
write.grayImage(data,'./cache/deep-orig.png')

The default setting of the functions plot.grayImage and write.grayImage is that the pixel intensities are encoded in 8 bits, which corresponds to 256 graylevels. Another number of graylevels may be specified through the optional argument cData=. The different possibilities are "bitmap" for binary images, "8bits" for 256 graylevel images (default), "10bits" for 1024 graylevel images and "12bits" for 4096 graylevel images.

The default setting of these functions also forces the pixel intensities to be in [0,1] in the following way. If some pixel intensities are negative, they are set to 0 (black). If some pixel intensities are larger than 1, they are set to 1 (white). An optional argument map="scaled" may be chosen. It forces the data to be in [0,1] by standardizing the graylevel intensity matrix.

Caution: R is not good at image displaying (as you can see on this page). That is why a recommended practice is that you write your image into a .png file and that you then open it with a suitable image viewer.

Plot the histogram of the image pixel intensities

The function hist.grayImage plots the histogram of the image pixel intensities. The default setting of this function is that the pixel intensities are encoded in 8 bits (that means 256 gray levels). Another number of graylevels may be specified through the optional argument cData=. The default setting of this function also forces the pixel intensities to be in [0,1] in the following way. If some pixel intensities are negative, they are set to 0 (black). If some pixel intensities are larger than 1, they are set to 1 (white). An optional argument map="scaled" may be chosen. It forces the data to be in [0,1] by standardizing the graylevel intensity matrix.

## Run this to plot the histogram of pixel intensities
hist.grayImage(data)

Reflectance estimation

To run our method and estimate an image reflectance, use the function illumination. The first input argument (data) is to be set as the matrix of pixel intensities. The second input argument (degree=) is the polynomial degree of the log-illumination function. The third input argument (method=) is the method chosen to estimate the gradient of the image. For the moment there are two choices for this latter argument. If the image is noise free, you can use the double finite difference method (methode="direct"). Otherwise you can use a local polynomial procedure (methode="gaussian") with a truncated Gaussian kernel.
The output of the function is a list consisting of different objects which are: the denoised image F, the reflectance estimation R, the illumination estimation L, the estimation of the polynomial coefficients of the log-illumination gamma, the parameters of the procedure pars, and the size of the image data.size.

# Run GKV estimation method
out <- illumination(data, degree=c(5,6), method='gaussian')
str(out)

Here the degree of the polynomial is set as (5,6). The parameter gamma is the matrix of polynomial coefficients of the log-illumination.

# Coefficients of the log-illumination polynome
round(out$gamma,2)

The gradient of the image is estimated by a local polynomial procedure (out$pars$gradient) with a truncated Gaussian kernel. The support of the truncated Gaussian kernel is of size 9 by 9 pixels. The bandwidth parameter is automatically chosen by cross-validation.

# Bandwidth parameter of local polynomials procedure which had been automatically chosen by the cross-validation procedure
out$pars$gradient$bandwidth

The three output images, that is the denoised image out$F, the estimated reflectance out$R and the estimated illumination out$L, can then be plotted.

Caution: do NOT use the optional argument map="scaled" when plotting the reflectance estimation. Indeed, our procedure is written to provide a consistent reflectance estimate the values of which are in [0,1]. If this is not the case, it indicates a convergence problem and and acts as a warning against non-reliable results.

# Plot the results:
# Plot the denoised image (but still with its illumination bias)
plot.grayImage(out$F)
# Plot the reflectance estimation
plot.grayImage(out$R)

Strictly speaking, the illumination image is not an image but a multiplicative factor of illumination effect. If the illumination at a given pixel is larger than 1, the ground truth intensity at this pixel is whiten by the illumination effect. If the illumination at a given pixel is less than 1, the ground truth intensity at this pixel is darken by the illumination effect. That is why we propose a level plot as illumination plot to get a visual impression of the repartition of the illumination effect.

# Plot the illumination estimation
plot.grayIllumination(out$L)
# Run this if you want to save the matrix out$R in the folder "figure" into a .png file
write.grayImage(out$R,'./figure/deep-R.png')
# Run this if you want to save the matrix out$L in the folder "figure" into a .png file
png(file="./figure/deep-L.png", width=ncol(out$L), height=(nrow(out$L)+50), res= 72)
plot.grayIllumination(out$L)
dev.off()

How to set the method parameters?

Estimation of the image gradient method=. If the image is noise free, both methods ("direct" and "gaussian") work and produce similar outputs. Note that the "gaussian" is more time-consuming because the smoothing parameter is chosen by cross-validation. If the image is noisy, the "gaussian" method provides results which are more consistant with the ground truth. The user may specify a pair of positive values for the smoothing parameter of the local polynomial procedure through the optional argument bandwidth= of the function illumination (in which case, no cross-validation procedure is done). The trick is that what we call the bandwidth is actually the inverse of bandwidth. Whatever, we advise the unexperienced user against the use of this optional argument.

# Run the GKV estimation method
out_direct <- illumination(data, degree=c(5,6), method='direct')
str(out_direct$pars)
plot.grayImage(out_direct$R)
plot.grayIllumination(out_direct$L)

Degree of the log-illumination polynome degree=. As of now, we do not propose any automatic procedure to select this argument. The user has to test several plausible values of pairs of positive integers. To this aim, we formulate some guidelines according to our experience. First, we highlight that the choice for this parameter is to be made within a (small) finite set. Indeed, if the degree of the polynome is chosen too large, the linear system that computes the parameter gamma of our procedure is singular. Secondly, an important point is the fact that a significant number of pixel intensity larger than 1 (sum(out$R>1)) Indeed, the reflectance to be recovered is supposed to be in [0,1], the estimate is proved to be consistent and no post-treatment is applied on the reflectance estimate out$R. Thus, if the estimated reflectance matrix gets a significant number of pixel intensity larger than 1 (sum(out$R>1)), this means that the choice of the argument degree is not well suited to the illumination artefact at hand.

# this is an example of the GKV estimation method run with an inconvenient degree choice
out_bad_degree <- illumination(data, degree=c(5,8), method='direct')

Our system is set in french hence the language of the error message. This error message says that the linear system to be solved is computationally singular and that its reciprocal condition number is far too small.


LS0tDQp0aXRsZTogJyoqR2VmZnJheSBTLiwgS2x1dGNobmlrb2ZmIE4uLCBWaW1vbmQgTS4gKDIwMTYpKiosIA0KICAqSWxsdW1pbmF0aW9uIHByb2JsZW1zIGluIGRpZ2l0YWwgaW1hZ2VzLiBBIHN0YXRpc3RpY2FsIHBvaW50IG9mIHZpZXcqLA0KICA8YSBocmVmPSJodHRwOi8vd3d3LnNjaWVuY2VkaXJlY3QuY29tL3NjaWVuY2UvYXJ0aWNsZS9waWkvUzAwNDcyNTlYMTYzMDAyMDMiIHRhcmdldD0iX2JsYW5rIj5Kb3VybmFsIG9mIE11bHRpdmFyaWF0ZSBBbmFseXNpcywgMTUwOiAxOTEtMjEzPC9hPicNCm91dHB1dDogaHRtbF9ub3RlYm9vaw0KLS0tDQoNCioqU3VtbWFyeSBvZiB0aGUgYXJ0aWNsZToqKiANCkludGVyZXN0IGlzIGZvY3VzZWQgb24gYSBtdWx0aS1kaW1lbnNpb25hbCBzaWduYWwgJFIkIG9ic2VydmVkIG9uIGEgZ3JpZCBpbiB0aGUgcHJlc2VuY2Ugb2YgYm90aCBhbiBpbGx1bWluYXRpb24gYXJ0aWZhY3QgYW5kIGFuIGFkZGl0aW9uYWwgYWRkaXRpdmUgYm91bmRlZCB2YXJpYW5jZSBjZW50ZXJlZCBub2lzZSAkXHZhcmVwc2lsb24uJCANClRoZSBpbGx1bWluYXRpb24gYXJ0aWZhY3QgY29uc2lzdHMgb2YgY29sb3VyIG9yIGdyZXkgbGV2ZWwgaW50ZW5zaXR5IHZhcmlhdGlvbnMgd2hpY2ggYXJlIHNlZW4gb24gdGhlIHNhbXBsZWQgaW1hZ2UgYnV0IHdoaWNoIGFyZSBub3QgcHJlc2VudCBpbiAkUi4kIA0KU3VjaCBhbiBhc3N1bXB0aW9uIGlzIGNsYXNzaWNhbGx5IG1vZGVsbGVkIHVzaW5nIGEgZnVuY3Rpb24gJEwkIHdoaWNoIGFjdHMgbXVsdGlwbGljYXRpdmVseSBvbiAkUi4kIA0KT3VyIGdvYWwgaXMgdG8gZXN0aW1hdGUgJFIkIGZyb20gb2JzZXJ2YXRpb25zIG9mIGEgcmFuZG9tIHZhcmlhYmxlICRZJCB3aGljaCBvYmV5cyB0aGUgcmVncmVzc2lvbiBtb2RlbCAkWT1STCtcdmFyZXBzaWxvbi4kIA0KV2UgaWRlbnRpZnkgYW5kIHByb3Bvc2UgYSBzb2x1dGlvbiB0byBhbiBpZGVudGlmaWFiaWxpdHkgaXNzdWUuIFdlIGNvbnN0cnVjdCBhIGNvbnNpc3RlbnQgbXVsdGktc3RlcCBlc3RpbWF0aW9uIHByb2NlZHVyZS4gDQpXZSBmaXJzdCBtYWtlIHVzZSBvZiBhbnkgY29uc2lzdGVudCBkZW5vaXNpbmcgbWV0aG9kIHRvIGVzdGltYXRlIGZyb20gdGhlIG5vaXN5IGRhdGEgdGhlIGdyYWRpZW50IG9mIHRoZSBsb2dhcml0aG0gb2YgdGhlIHJlZ3Jlc3Npb24gZnVuY3Rpb24uIA0KV2UgYXNzdW1lIHRoYXQgdGhlIGFydGVmYWN0ICRMJCBjb25zaXN0cyBvZiAic21vb3RoIiB2YXJpYXRpb25zLiANClRoZW4gd2UgcHJvamVjdCB0aGUgZGVub2lzZWQgYXV4aWxpYXJ5IGVzdGltYXRlIG9uIGEgZmluaXRlIGJhc2lzIG9mICJzbW9vdGgiIGZ1bmN0aW9ucy4gDQpXZSBkZWR1Y2UgdGhlIGZpbmFsIGVzdGltYXRvciBvZiAkUiQgc28gdGhhdCB0aGUgcHJvcG9zZWQgaWRlbnRpZmlhYmlsaXR5IGNvbnN0cmFpbnQgaXMgc2F0aXNmaWVkLiANCldlIGRlcml2ZSBhbiB1cHBlciBib3VuZCBmb3IgdGhlIHN1cC1ub3JtIHJpc2sgb2Ygb3VyIGVzdGltYXRvci4gDQpBcHBsaWNhdGlvbnMgdG8gZGlmZmVyZW50IGltYWdlcyBhcmUgcHJlc2VudGVkLg0KDQoqKkFpbSBvZiB0aGlzIHBhZ2U6KiogaGVscCB5b3UgZmluZCB5b3VyIG93biB3YXkgd2l0aCBvdXIgcHVibGljbHkgYXZhaWxhYmxlIGNvZGUhDQoNCioqSGFyZHdhcmUgcmVxdWlyZW1lbnQ6KiogdG8gcnVuIG91ciBjb2RlIG9uIHlvdXIgZGF0YSwgeW91IHNob3VsZCBiZSB3b3JraW5nIHdpdGggdGhlIGxhdGVzdCBmcmVlIG9wZW4gc291cmNlIFIgc29mdHdhcmUgKDxhIGhyZWY9Imh0dHA6Ly93d3cuci1wcm9qZWN0Lm9yZyIgdGFyZ2V0PSJfYmxhbmsiPnd3dy5yLXByb2plY3Qub3JnPC9hPikgPGZvbnQgY29sb3I9IiNFNDFCMTciPiBvciBhdCBsZWFzdCB3aXRoIFIgVmVyc2lvbiAzLjMuMTwvZm9udD4gaW5zdGFsbGVkIG9uIGl0LiANCk1vcmVvdmVyLCB5b3VyIHN5c3RlbSBzaG91bGQgYmUgcnVuIDxmb250IGNvbG9yPSIjRTQxQjE3Ij4gYXQgbGVhc3QgdW5kZXIgd2luZG93cyBzZXZlbiBvciBVYnVudHUgMTAuNCBvciBNYWMgT1MgWCBFbCBDYXBpdGFuIDEwLjExLjE8L2ZvbnQ+Lg0KDQpJZiB5b3VyIGFyZSBhIGJlZ2lubmVyIFIgdXNlciwgdGhlbiBmaXJzdCBnbyB0byBvdXIgPGEgaHJlZj0iaGVscFIuaHRtbCIgdGFyZ2V0PSJfYmxhbmsiPmhlbHAgcGFnZTwvYT4uIFlvdSBjYW4gc2tpcCBpdCwgaWYgeW91IGFscmVhZHkga25vdyBob3cgdG8gdXNlIFIuDQoNCkRvd25sb2FkIG91ciBjb2RlOjwvc3Ryb25nPiBEb3dubG9hZCB0aGUgY29tcHJlc3NlZCBmaWxlIGluIHRoZSBwcmVmZXJlZCBmb3JtYXQgYW5kIGV4dHJhY3QgdGhlIGFyY2hpdmUgaW50byBhIG5ldyBlbXB0eSByZXBlcnRvcnkgKG5hbWVkIGFzICJHS1YiIHRocm91Z2hvdXQgdGhpcyB0dXRvcmlhbCk6IA0KDQoqIFtvdXIgUiBjb2RlIGluIGEgLnppcCBmaWxlXSguL3NvZnR3YXJlL2drdi56aXApDQoqIFtvdXIgUiBjb2RlIGluIGEgLnRhciBmaWxlXSguL3NvZnR3YXJlL2drdi50YXIpDQoNCioqQ29udGVudHMgb2YgdGhlIGFyY2hpdmU6KiogSGVyZSBpcyBhIHNjaGVtYXRpYyBkZXNjcmlwdGlvbiBvZiB0aGUgY29udGVudHMgb2YgdGhlIGZpbGUuIA0KPGNlbnRlcj4gDQohW0ZpbGUgc3RydWN0dXJlXSguL3NvZnR3YXJlL291ci1maWxlLXRyZWUucG5nKQ0KPC9jZW50ZXI+DQpPdXIgY29kZSBpcyB3cml0dGVuIHRvIGJlIHVzZWQgYWNjb3JkaW5nIHRvIHRoZSBmb2xsb3dpbmcgZ3VpZGVsaW5lczogDQoNCjEuIFNldCBSIHdvcmtpbmcgZGlyZWN0b3J5DQoyLiBMb2FkIGFkZGl0aW9uYWwgcGFja2FnZXMNCjMuIExvYWQgeW91ciBkYXRhIGluIFIgd29ya3NwYWNlLg0KICAgSW1hZ2VzIGhhdmUgdG8gYmUgaW4gdGhlIC5wbmcgZm9ybWF0Lg0KNC4gWW91IGNhbiBkbyBzb21lIHByZWxpbWluYXJ5IGFuYWx5c2lzIChwbG90IHlvdXIgZGF0YSwgcGxvdCBncmF5bGV2ZWwgaGlzdG9ncmFtLCBjaGVjayBzb21lIHBpeGVsIHZhbHVlcywgY2hlY2sgZGF0YSBzaXplKQ0KNS4gQ2FsbCB0aGUgbWFpbiBmdW5jdGlvbiBgYGBzYW1wbGUtYW5hbHlzaXMuUmBgYCAoYW5kIG1vZGlmeSBpdCBpZiBuZWVkZWQpLg0KNC4gUGxvdCB0aGUgZGVub2lzZWQgaW1hZ2UuDQo1LiBQbG90IHRoZSBpbGx1bWluYXRpb24gZXN0aW1hdGlvbiAoYWN0dWFsbHkgeW91IGdldCBhIGxldmVsIHBsb3Qgb2YgdGhlIGlsbHVtaW5hdGlvbikuDQo2LiBQbG90IHRoZSByZWZsZWN0YW5jZSBlc3RpbWF0aW9uICh0aGlzIGlzIHlvdXIgcHJvY2Vzc2VkIGltYWdlIHdoaWNoIGlzIGZyZWUgb2YgYm90aCBub2lzZSBhbmQgaWxsdW1pbmF0aW9uKS4gIA0KICANCiAgICANCiAgDQoNCiMjIEdldCBSIHdvcmtzcGFjZSByZWFkeSANCg0KDQojIyMjIFNldCBSIHdvcmtpbmcgZGlyZWN0b3J5DQpPdXIgY29kZSBpcyB3cml0dGVuIHRvIHdvcmsgd2l0aCAiR0tWIiBhcyBSIHdvcmtpbmcgZGlyZWN0b3J5Lg0KYGBge3IgZXZhbD1GQUxTRX0NCiMgU3BlY2lmeSB0aGUgcGF0aCB0byBSIHdvcmtpbmcgZGlyZWN0b3J5DQp3b3JrRGlyIDwtICIveW91ci13b3JrRGlyLXBhdGgtdG8vR0tWIg0Kc2V0d2Qod29ya0RpcikNCmBgYA0KDQogIA0KIyMjIyBMb2FkIGFkZGl0aW9uYWwgcGFja2FnZXMNClRoZSBmb2xsb3dpbmcgcGFja2FnZXMgYXJlIHJlcXVpcmVkOg0KPGEgaHJlZj0iaHR0cHM6Ly9jcmFuLnItcHJvamVjdC5vcmcvd2ViL3BhY2thZ2VzL3BuZy9pbmRleC5odG1sIiB0YXJnZXQ9Il9ibGFuayI+KipwbmcqKjwvYT4sIDxhIGhyZWY9Imh0dHBzOi8vY3Jhbi5yLXByb2plY3Qub3JnL3dlYi9wYWNrYWdlcy9yYXN0ZXJWaXMvaW5kZXguaHRtbCIgdGFyZ2V0PSJfYmxhbmsiPioqcmFzdGVyVmlzKio8L2E+LiANCkNoZWNrIHRoYXQgdGhlc2UgcGFja2FnZXMgYXJlIGluc3RhbGxlZCBvbiB5b3VyIHN5c3RlbS4NCklmIG5vdCwgaW5zdGFsbCB0aGVtIGJlZm9yZSBsb2FkaW5nIHRoZW0uDQpgYGB7ciBlY2hvPVRSVUUsIHdhcm5pbmc9RkFMU0UsIHJlc3VsdHM9J2hpZGUnLCBtZXNzYWdlPUZBTFNFfQ0KIyBMb2FkIHJlcXVpcmVkIHBhY2thZ2VzIGZvciBpbWFnZSBtYW5pcHVsYXRpb24NCnJlcXVpcmUocG5nKQ0KcmVxdWlyZShyYXN0ZXJWaXMpDQpgYGANCg0KSWYgeW91IGFyZSB3b3JraW5nIG9mZi1saW5lLCBub3RlIHRoYXQgdGhlICoqcmFzdGVyVmlzKiogcGFja2FnZSBkZXBlbmRzIG9uIG90aGVyIHBhY2thZ2VzIHRoYXQgeW91IGFsc28gaGF2ZSB0byBpbnN0YWxsOiA8YSBocmVmPSJodHRwczovL2NyYW4uci1wcm9qZWN0Lm9yZy93ZWIvcGFja2FnZXMvcmFzdGVyL2luZGV4Lmh0bWwiIHRhcmdldD0iX2JsYW5rIj4qKnJhc3RlcioqPC9hPiwgPGEgaHJlZj0iaHR0cHM6Ly9jcmFuLnItcHJvamVjdC5vcmcvd2ViL3BhY2thZ2VzL2xhdHRpY2UvaW5kZXguaHRtbCIgdGFyZ2V0PSJfYmxhbmsiPioqbGF0dGljZSoqPC9hPiwgPGEgaHJlZj0iaHR0cHM6Ly9jcmFuLnItcHJvamVjdC5vcmcvd2ViL3BhY2thZ2VzL3NwL2luZGV4Lmh0bWwiIHRhcmdldD0iX2JsYW5rIj4qKnNwKio8L2E+LA0KPGEgaHJlZj0iaHR0cHM6Ly9jcmFuLnItcHJvamVjdC5vcmcvd2ViL3BhY2thZ2VzL2xhdHRpY2VFeHRyYS9pbmRleC5odG1sIiB0YXJnZXQ9Il9ibGFuayI+KipsYXR0aWNlRXh0cmEqKjwvYT4sDQo8YSBocmVmPSJodHRwczovL2NyYW4uci1wcm9qZWN0Lm9yZy93ZWIvcGFja2FnZXMvUkNvbG9yQnJld2VyL2luZGV4Lmh0bWwiIHRhcmdldD0iX2JsYW5rIj4qKlJDb2xvckJyZXdlcioqPC9hPi4NCg0KICAgIA0KIyMjIyBMb2FkIHRoZSB0ZWNobmljYWwgZnVuY3Rpb25zDQpGaXJzdCwgbG9hZCB0aGUgdGVjaG5pY2FsIGZ1bmN0aW9ucyBpbnRvIFIgd29ya3NwYWNlIGFzIHNob3duIGJlbG93Lg0KVGhlIHNjcmlwdCBmaWxlIGBgYGZuY3QwMS1ncmF5SW1hZ2UuUmBgYCBjb250YWlucyB0aGUgY29kZSB3aGljaCByZWFkcywgd3JpdGVzIGFuZCBwbG90cyBhIGdyYXlsZXZlbCBpbWFnZS4NClRoZSBzY3JpcHQgZmlsZSBgYGBmbmN0MDQtZ2t2LWdyYXkuUmBgYCBjb21wdXRlcyB0aGUgaWxsdW1pbmF0aW9uIGFuZCB0aGUgcmVmbGVjdGFuY2UgZXN0aW1hdGVzLg0KVG8gdGhpcyBhaW0sIGl0IGNhbGxzIG9uIHRoZSB0ZWNobmljYWwgZnVuY3Rpb25zIGBgYGZuY3QwMi1na3YtdXRpbHMuUmBgYCBhbmQgYGBgZm5jdDAzLWdrdi1ncmFkcy5SYGBgLg0KYGBge3J9DQojIFJ1biB1c2VyLWRlZmluZWQgZnVuY3Rpb25zDQpzb3VyY2UoJy4vbGliL2ZuY3QwMS1ncmF5SW1hZ2UuUicpDQpzb3VyY2UoJy4vbGliL2ZuY3QwNC1na3YtZ3JheS5SJykNCmBgYCAgIA0KICAgDQoNCiAgDQogICAgDQojIyBMb2FkIGFuZCBtYW5pcHVsYXRlIHlvdXIgaW1hZ2UgZGF0YSBpbiBSIHdvcmtzcGFjZQ0KDQojIyMjIFJlYWQsIHBsb3QgYW5kIHdyaXRlIGFuIGltYWdlIHdpdGggZGVmYXVsdCBvcHRpb25zDQpMZXQgYGBgZGVlcC5wbmdgYGAgYmUgdGhlICIucG5nIiBpbWFnZSB3ZSBhcmUgdXNpbmcgYXMgYW4gaWxsdXN0cmF0ZWQgZXhhbXBsZS4NCg0KVG8gbG9hZCBhbiBpbWFnZSBpbnRvIFIgd29ya3NwYWNlLCB1c2UgdGhlIGZ1bmN0aW9uIGBgYHJlYWQuZ3JheUltYWdlYGBgLg0KVGhlIG91dHB1dCBvZiB0aGUgZnVuY3Rpb24gYGBgcmVhZC5ncmF5SW1hZ2VgYGAgaXMgYSBtYXRyaXggY29udGFpbmluZyB0aGUgaW1hZ2UgcGl4ZWwgZ3JheWxldmVsIGludGVuc2l0aWVzLiANClRoZSBpbnRlbnNpdGllcyBiZWxvbmcgdG8gdGhlIGludGVydmFsIFswLDFdIHdoZXJlIHRoZSB2YWx1ZSAwIHN0YW5kcyBmb3IgdGhlIGJsYWNrIGNvbG9yIGFuZCB0aGUgdmFsdWUgMSBzdGFuZHMgZm9yIHRoZSB3aGl0ZSBjb2xvci4NCkluIG91ciBpbGx1c3RyYXRlZCBleGFtcGxlLCB0aGUgbWF0cml4IG9mIHBpeGVsIGludGVuc2l0aWVzIGlzIHRoZW4gc3RvcmVkIGluIHRoZSBvYmplY3QgbmFtZWQgYXMgYGBgZGF0YWBgYC4NCmBgYHtyfQ0KIyBSdW4gdGhlIHJlYWQuZ3JheUltYWdlIGZ1bmN0aW9uIA0KZGF0YSA8LSByZWFkLmdyYXlJbWFnZSgnLi9kYXRhL2RlZXAucG5nJykNCiMgUnVuIHRoaXMgaWYgeW91IHdhbnQgdG8gY2hlY2sgb3IgbmVlZCB0aGUgaW1hZ2Ugc2l6ZQ0KbjEgPC0gZGltKGRhdGEpWzFdICMgbnVtYmVyIG9mIHJvd3MNCm4yIDwtIGRpbShkYXRhKVsyXSAjIG51bWJlciBvZiBjb2x1bW5zDQpuMTsgbjINCmBgYA0KVG8gcGxvdCB0aGlzIGltYWdlIChvbmNlIGl0IGhhcyBiZWVuIHJlYWQpLCB1c2UgdGhlIGZ1bmN0aW9uIGBgYHBsb3QuZ3JheUltYWdlYGBgLg0KQnkgZGVmYXVsdCwgaXQgaXMgb25seSBuZWNlc3NhcnkgdG8gc3BlY2lmeSB0aGUgcGF0aCB0byB0aGUgaW1hZ2UgbG9jYXRpb24gZnJvbSBSIHdvcmtpbmcgZGlyZWN0b3J5LiANCmBgYHtyIGZpZy53aWR0aD02LCBmaWcuaGVpZ2h0PTUuNX0NCiMgUnVuIHRoZSBwbG90LmdyYXlJbWFnZSBmdW5jdGlvbiANCnBsb3QuZ3JheUltYWdlKGRhdGEpDQpgYGAgICANCiAgIA0KDQoNCiAgIA0KIyMjIyBSZWFkLCBwbG90IGFuZCB3cml0ZSBhbiBpbWFnZSB3aXRoIHVzZXItc3BlY2lmaWVkIG9wdGlvbnMNClRvIGFwcGx5IG91ciBtZXRob2Qgb24gdGhpcyBpbWFnZSwgdGhlIGltYWdlIGxlZ2VuZCBuZWVkcyB0byBiZSBjdXQgb2ZmLg0KSW4gZmFjdCwgdGhlIHJlZ2lvbiBvZiBpbnRlcmVzdCBvZiB0aGUgaW1hZ2UgYGBgZGVlcC5wbmdgYGAgY29uc2lzdHMgb2YgYWxsIHRoZSBwaXhlbCBjb2x1bW5zIGFuZCB0aGUgcGl4ZWwgcm93cyBmcm9tIDEgdG8gNDIyLg0KYGBge3J9DQojIExvYWQgZGF0YSBhbmQgb3ZlcndyaXRlIHRoZSAiZGF0YSIiIG9iamVjdA0KZGF0YSA8LSByZWFkLmdyYXlJbWFnZSgnLi9kYXRhL2RlZXAucG5nJywgbnJvdz0xOjQyMiwgbmNvbD1OVUxMKQ0KIyBSdW4gdGhpcyBpZiB5b3Ugd2FudCB0byBjaGVjayBvciBuZWVkIHRoZSBzaXplIG9mIHRoZSBsb2FkZWQgcGFydCBvZiB0aGUgaW1hZ2UgDQpuMSA8LSBkaW0oZGF0YSlbMV0gIyBudW1iZXIgb2Ygcm93cw0KbjIgPC0gZGltKGRhdGEpWzJdICMgbnVtYmVyIG9mIGNvbHVtbnMNCm4xOyBuMg0KYGBgDQpgYGB7ciBmaWcud2lkdGg9NiwgZmlnLmhlaWdodD01LjF9DQojIFJ1biB0aGlzIGlmIHlvdSB3YW50IHRvIHBsb3QgdGhlIHRyaW1tZWQgaW1hZ2UNCnBsb3QuZ3JheUltYWdlKGRhdGEpDQpgYGANCk1vcmUgZ2VuZXJhbGx5LCBhIHJlY3Rhbmd1bGFyIHN1YnJlZ2lvbiBvZiBpbnRlcmVzdCBtYXkgYmUgZGVmaW5lZCBieSB0aGUgb3B0aW9uYWwgYXJndW1lbnQgYGBgbnJvdz1gYGAgYW5kIGBgYG5jb2w9YGBgLg0KRm9yIGluc3RhbmNlLCBpZiB0aGUgcmVnaW9uIG9mIGludGVyZXN0IGlzIGEgcmVjdGFuZ2xlIGRlZmluZWQgYnkgdGhlIHBpeGVsIHJvd3MgZnJvbSAxMCB0byAzMCBhbmQgYnkgdGhlIHBpeGVscyBjb2x1bW5zIGZyb20gNTAgdG8gNzAsIHRoZW4gZG86DQpgYGB7ciBmaWcud2lkdGg9NCwgZmlnLmhlaWdodD0zfQ0KIyBSdW4gdGhlIHJlYWQuZ3JheUltYWdlIGZ1bmN0aW9uIG9uIGEgc3ViaW1hZ2UNCnN1YmRhdGEgPC0gcmVhZC5ncmF5SW1hZ2UoJy4vZGF0YS9kZWVwLnBuZycsIG5yb3c9MTA6MzAsIG5jb2w9NTA6NzApDQpwbG90LmdyYXlJbWFnZShzdWJkYXRhKQ0KYGBgDQpUaGUgb2JqZWN0IGBgYGRhdGFgYGAgbWF5IGJlIHdyaXR0ZW4gaW50byBhIC5wbmcgZmlsZSB3aXRoIHRoZSBmdW5jdGlvbiBgYGB3cml0ZS5ncmF5SW1hZ2VgYGAuDQpgYGB7ciBldmFsPUZBTFNFfQ0KIyBSdW4gdGhpcyBpZiB5b3Ugd2FudCB0byBzYXZlIHRoZSBkYXRhIG1hdHJpeCBpbiB0aGUgZm9sZGVyICJjYWNoZSIgaW50byBhIC5wbmcgZmlsZQ0Kd3JpdGUuZ3JheUltYWdlKGRhdGEsJy4vY2FjaGUvZGVlcC1vcmlnLnBuZycpDQpgYGANClRoZSBkZWZhdWx0IHNldHRpbmcgb2YgdGhlIGZ1bmN0aW9ucyBgYGBwbG90LmdyYXlJbWFnZWBgYCBhbmQgYGBgd3JpdGUuZ3JheUltYWdlYGBgIGlzIHRoYXQgdGhlIHBpeGVsIGludGVuc2l0aWVzIGFyZSBlbmNvZGVkIGluIDggYml0cywgd2hpY2ggY29ycmVzcG9uZHMgdG8gMjU2IGdyYXlsZXZlbHMuIA0KQW5vdGhlciBudW1iZXIgb2YgZ3JheWxldmVscyBtYXkgYmUgc3BlY2lmaWVkIHRocm91Z2ggdGhlIG9wdGlvbmFsIGFyZ3VtZW50IGBgYGNEYXRhPWBgYC4NClRoZSBkaWZmZXJlbnQgcG9zc2liaWxpdGllcyBhcmUgYGBgImJpdG1hcCJgYGAgZm9yIGJpbmFyeSBpbWFnZXMsIGBgYCI4Yml0cyJgYGAgZm9yIDI1NiBncmF5bGV2ZWwgaW1hZ2VzIChkZWZhdWx0KSwgYGBgIjEwYml0cyJgYGAgZm9yIDEwMjQgZ3JheWxldmVsIGltYWdlcyBhbmQgYGBgIjEyYml0cyJgYGAgZm9yIDQwOTYgZ3JheWxldmVsIGltYWdlcy4NCg0KVGhlIGRlZmF1bHQgc2V0dGluZyBvZiB0aGVzZSBmdW5jdGlvbnMgYWxzbyBmb3JjZXMgdGhlIHBpeGVsIGludGVuc2l0aWVzIHRvIGJlIGluIFswLDFdIGluIHRoZSBmb2xsb3dpbmcgd2F5LiANCklmIHNvbWUgcGl4ZWwgaW50ZW5zaXRpZXMgYXJlIG5lZ2F0aXZlLCB0aGV5IGFyZSBzZXQgdG8gMCAoYmxhY2spLg0KSWYgc29tZSBwaXhlbCBpbnRlbnNpdGllcyBhcmUgbGFyZ2VyIHRoYW4gMSwgdGhleSBhcmUgc2V0IHRvIDEgKHdoaXRlKS4NCkFuIG9wdGlvbmFsIGFyZ3VtZW50IGBgYG1hcD0ic2NhbGVkImBgYCBtYXkgYmUgY2hvc2VuLiANCkl0IGZvcmNlcyB0aGUgZGF0YSB0byBiZSBpbiBbMCwxXSBieSBzdGFuZGFyZGl6aW5nIHRoZSBncmF5bGV2ZWwgaW50ZW5zaXR5IG1hdHJpeC4NCg0KDQoNCioqPGZvbnQgY29sb3I9IiNFNDFCMTciPkNhdXRpb248L2ZvbnQ+Kio6IFIgaXMgbm90IGdvb2QgYXQgaW1hZ2UgZGlzcGxheWluZyAoYXMgeW91IGNhbiBzZWUgb24gdGhpcyBwYWdlKS4NClRoYXQgaXMgd2h5IGEgcmVjb21tZW5kZWQgcHJhY3RpY2UgaXMgdGhhdCB5b3Ugd3JpdGUgeW91ciBpbWFnZSBpbnRvIGEgLnBuZyBmaWxlIGFuZCB0aGF0IHlvdSB0aGVuIG9wZW4gaXQgd2l0aCBhIHN1aXRhYmxlIGltYWdlIHZpZXdlci4NCg0KDQogIA0KIyMjIyBQbG90IHRoZSBoaXN0b2dyYW0gb2YgdGhlIGltYWdlIHBpeGVsIGludGVuc2l0aWVzDQpUaGUgZnVuY3Rpb24gYGBgaGlzdC5ncmF5SW1hZ2VgYGAgcGxvdHMgdGhlIGhpc3RvZ3JhbSBvZiB0aGUgaW1hZ2UgcGl4ZWwgaW50ZW5zaXRpZXMuDQpUaGUgZGVmYXVsdCBzZXR0aW5nIG9mIHRoaXMgZnVuY3Rpb24gaXMgdGhhdCB0aGUgcGl4ZWwgaW50ZW5zaXRpZXMgYXJlIGVuY29kZWQgaW4gOCBiaXRzICh0aGF0IG1lYW5zIDI1NiBncmF5IGxldmVscykuIA0KQW5vdGhlciBudW1iZXIgb2YgZ3JheWxldmVscyBtYXkgYmUgc3BlY2lmaWVkIHRocm91Z2ggdGhlIG9wdGlvbmFsIGFyZ3VtZW50IGBgYGNEYXRhPWBgYC4NClRoZSBkZWZhdWx0IHNldHRpbmcgb2YgdGhpcyBmdW5jdGlvbiBhbHNvIGZvcmNlcyB0aGUgcGl4ZWwgaW50ZW5zaXRpZXMgdG8gYmUgaW4gWzAsMV0gaW4gdGhlIGZvbGxvd2luZyB3YXkuIA0KSWYgc29tZSBwaXhlbCBpbnRlbnNpdGllcyBhcmUgbmVnYXRpdmUsIHRoZXkgYXJlIHNldCB0byAwIChibGFjaykuDQpJZiBzb21lIHBpeGVsIGludGVuc2l0aWVzIGFyZSBsYXJnZXIgdGhhbiAxLCB0aGV5IGFyZSBzZXQgdG8gMSAod2hpdGUpLg0KQW4gb3B0aW9uYWwgYXJndW1lbnQgYGBgbWFwPSJzY2FsZWQiYGBgIG1heSBiZSBjaG9zZW4uIA0KSXQgZm9yY2VzIHRoZSBkYXRhIHRvIGJlIGluIFswLDFdIGJ5IHN0YW5kYXJkaXppbmcgdGhlIGdyYXlsZXZlbCBpbnRlbnNpdHkgbWF0cml4Lg0KYGBge3IgZmlnLndpZHRoPTcsIGZpZy5oZWlnaHQ9Nn0NCiMjIFJ1biB0aGlzIHRvIHBsb3QgdGhlIGhpc3RvZ3JhbSBvZiBwaXhlbCBpbnRlbnNpdGllcw0KaGlzdC5ncmF5SW1hZ2UoZGF0YSkNCmBgYCAgDQogICANCiAgDQogICAgDQojIyBSZWZsZWN0YW5jZSBlc3RpbWF0aW9uDQpUbyBydW4gb3VyIG1ldGhvZCBhbmQgZXN0aW1hdGUgYW4gaW1hZ2UgcmVmbGVjdGFuY2UsIHVzZSB0aGUgZnVuY3Rpb24gYGBgaWxsdW1pbmF0aW9uYGBgLg0KVGhlIGZpcnN0IGlucHV0IGFyZ3VtZW50IChgYGBkYXRhYGBgKSBpcyB0byBiZSBzZXQgYXMgdGhlICBtYXRyaXggb2YgcGl4ZWwgaW50ZW5zaXRpZXMuIA0KVGhlIHNlY29uZCBpbnB1dCBhcmd1bWVudCAoYGBgZGVncmVlPWBgYCkgaXMgdGhlIHBvbHlub21pYWwgZGVncmVlIG9mIHRoZSBsb2ctaWxsdW1pbmF0aW9uIGZ1bmN0aW9uLg0KVGhlIHRoaXJkIGlucHV0IGFyZ3VtZW50IChgYGBtZXRob2Q9YGBgKSBpcyB0aGUgbWV0aG9kIGNob3NlbiB0byBlc3RpbWF0ZSB0aGUgZ3JhZGllbnQgb2YgdGhlIGltYWdlLg0KRm9yIHRoZSBtb21lbnQgdGhlcmUgYXJlIHR3byBjaG9pY2VzIGZvciB0aGlzIGxhdHRlciBhcmd1bWVudC4gDQpJZiB0aGUgaW1hZ2UgaXMgbm9pc2UgZnJlZSwgeW91IGNhbiB1c2UgdGhlIGRvdWJsZSBmaW5pdGUgZGlmZmVyZW5jZSBtZXRob2QgKGBgYG1ldGhvZGU9ImRpcmVjdCJgYGApLiANCk90aGVyd2lzZSB5b3UgY2FuIHVzZSBhIGxvY2FsIHBvbHlub21pYWwgcHJvY2VkdXJlIChgYGBtZXRob2RlPSJnYXVzc2lhbiJgYGApIHdpdGggYSB0cnVuY2F0ZWQgR2F1c3NpYW4ga2VybmVsLiAgIA0KVGhlIG91dHB1dCBvZiB0aGUgZnVuY3Rpb24gaXMgYSBsaXN0IGNvbnNpc3Rpbmcgb2YgZGlmZmVyZW50IG9iamVjdHMgd2hpY2ggYXJlOiB0aGUgZGVub2lzZWQgaW1hZ2UgYGBgRmBgYCwgdGhlIHJlZmxlY3RhbmNlIGVzdGltYXRpb24gYGBgUmBgYCwgdGhlIGlsbHVtaW5hdGlvbiBlc3RpbWF0aW9uIGBgYExgYGAsIHRoZSBlc3RpbWF0aW9uIG9mIHRoZSBwb2x5bm9taWFsIGNvZWZmaWNpZW50cyBvZiB0aGUgbG9nLWlsbHVtaW5hdGlvbiBgYGBnYW1tYWBgYCwgdGhlIHBhcmFtZXRlcnMgb2YgdGhlIHByb2NlZHVyZSBgYGBwYXJzYGBgLCBhbmQgdGhlIHNpemUgb2YgdGhlIGltYWdlIGBgYGRhdGEuc2l6ZWBgYC4gIA0KYGBge3J9DQojIFJ1biBHS1YgZXN0aW1hdGlvbiBtZXRob2QNCm91dCA8LSBpbGx1bWluYXRpb24oZGF0YSwgZGVncmVlPWMoNSw2KSwgbWV0aG9kPSdnYXVzc2lhbicpDQpzdHIob3V0KQ0KYGBgDQpIZXJlIHRoZSBkZWdyZWUgb2YgdGhlIHBvbHlub21pYWwgaXMgc2V0IGFzICg1LDYpLg0KVGhlIHBhcmFtZXRlciBgYGBnYW1tYWBgYCBpcyB0aGUgbWF0cml4IG9mIHBvbHlub21pYWwgY29lZmZpY2llbnRzIG9mIHRoZSBsb2ctaWxsdW1pbmF0aW9uLg0KYGBge3J9DQojIENvZWZmaWNpZW50cyBvZiB0aGUgbG9nLWlsbHVtaW5hdGlvbiBwb2x5bm9tZQ0Kcm91bmQob3V0JGdhbW1hLDIpDQpgYGANCg0KVGhlIGdyYWRpZW50IG9mIHRoZSBpbWFnZSBpcyBlc3RpbWF0ZWQgYnkgYSBsb2NhbCBwb2x5bm9taWFsIHByb2NlZHVyZSAoYGBgb3V0JHBhcnMkZ3JhZGllbnRgYGApIHdpdGggYSB0cnVuY2F0ZWQgR2F1c3NpYW4ga2VybmVsLiANClRoZSBzdXBwb3J0IG9mIHRoZSB0cnVuY2F0ZWQgR2F1c3NpYW4ga2VybmVsIGlzIG9mIHNpemUgOSBieSA5IHBpeGVscy4gDQpUaGUgYmFuZHdpZHRoIHBhcmFtZXRlciBpcyBhdXRvbWF0aWNhbGx5IGNob3NlbiBieSBjcm9zcy12YWxpZGF0aW9uLg0KYGBge3J9DQojIEJhbmR3aWR0aCBwYXJhbWV0ZXIgb2YgbG9jYWwgcG9seW5vbWlhbHMgcHJvY2VkdXJlIHdoaWNoIGhhZCBiZWVuIGF1dG9tYXRpY2FsbHkgY2hvc2VuIGJ5IHRoZSBjcm9zcy12YWxpZGF0aW9uIHByb2NlZHVyZQ0Kb3V0JHBhcnMkZ3JhZGllbnQkYmFuZHdpZHRoDQpgYGANClRoZSB0aHJlZSBvdXRwdXQgaW1hZ2VzLCB0aGF0IGlzIHRoZSBkZW5vaXNlZCBpbWFnZSBgYGBvdXQkRmBgYCwgdGhlIGVzdGltYXRlZCByZWZsZWN0YW5jZSBgYGBvdXQkUmBgYCBhbmQgdGhlIGVzdGltYXRlZCBpbGx1bWluYXRpb24gYGBgb3V0JExgYGAsIGNhbiB0aGVuIGJlIHBsb3R0ZWQuDQoNCkNhdXRpb246IGRvIE5PVCB1c2UgdGhlIG9wdGlvbmFsIGFyZ3VtZW50IGBgYG1hcD0ic2NhbGVkImBgYCB3aGVuIHBsb3R0aW5nIHRoZSByZWZsZWN0YW5jZSBlc3RpbWF0aW9uLg0KSW5kZWVkLCBvdXIgcHJvY2VkdXJlIGlzIHdyaXR0ZW4gdG8gcHJvdmlkZSBhIGNvbnNpc3RlbnQgcmVmbGVjdGFuY2UgZXN0aW1hdGUgdGhlIHZhbHVlcyBvZiB3aGljaCBhcmUgaW4gWzAsMV0uDQpJZiB0aGlzIGlzIG5vdCB0aGUgY2FzZSwgaXQgaW5kaWNhdGVzIGEgY29udmVyZ2VuY2UgcHJvYmxlbSBhbmQgYW5kIGFjdHMgYXMgYSB3YXJuaW5nIGFnYWluc3Qgbm9uLXJlbGlhYmxlIHJlc3VsdHMuDQoNCmBgYHtyIGZpZy53aWR0aD03LCBmaWcuaGVpZ2h0PTYsIHdhcm5pbmc9RkFMU0V9DQojIFBsb3QgdGhlIHJlc3VsdHM6DQojIFBsb3QgdGhlIGRlbm9pc2VkIGltYWdlIChidXQgc3RpbGwgd2l0aCBpdHMgaWxsdW1pbmF0aW9uIGJpYXMpDQpwbG90LmdyYXlJbWFnZShvdXQkRikNCiMgUGxvdCB0aGUgcmVmbGVjdGFuY2UgZXN0aW1hdGlvbg0KcGxvdC5ncmF5SW1hZ2Uob3V0JFIpDQpgYGANCg0KDQpTdHJpY3RseSBzcGVha2luZywgdGhlIGlsbHVtaW5hdGlvbiBpbWFnZSBpcyBub3QgYW4gaW1hZ2UgYnV0IGEgbXVsdGlwbGljYXRpdmUgZmFjdG9yIG9mIGlsbHVtaW5hdGlvbiBlZmZlY3QuIA0KSWYgdGhlIGlsbHVtaW5hdGlvbiBhdCBhIGdpdmVuIHBpeGVsIGlzIGxhcmdlciB0aGFuIDEsIHRoZSBncm91bmQgdHJ1dGggaW50ZW5zaXR5IGF0IHRoaXMgcGl4ZWwgaXMgd2hpdGVuIGJ5IHRoZSBpbGx1bWluYXRpb24gZWZmZWN0LiANCklmIHRoZSBpbGx1bWluYXRpb24gYXQgYSBnaXZlbiBwaXhlbCBpcyBsZXNzIHRoYW4gMSwgdGhlIGdyb3VuZCB0cnV0aCBpbnRlbnNpdHkgYXQgdGhpcyBwaXhlbCBpcyBkYXJrZW4gYnkgdGhlIGlsbHVtaW5hdGlvbiBlZmZlY3QuDQpUaGF0IGlzIHdoeSB3ZSBwcm9wb3NlIGEgbGV2ZWwgcGxvdCBhcyBpbGx1bWluYXRpb24gcGxvdCB0byBnZXQgYSB2aXN1YWwgaW1wcmVzc2lvbiBvZiB0aGUgcmVwYXJ0aXRpb24gb2YgdGhlIGlsbHVtaW5hdGlvbiBlZmZlY3QuDQoNCmBgYHtyIGZpZy53aWR0aD03LCBmaWcuaGVpZ2h0PTYsIHdhcm5pbmc9RkFMU0V9DQojIFBsb3QgdGhlIGlsbHVtaW5hdGlvbiBlc3RpbWF0aW9uDQpwbG90LmdyYXlJbGx1bWluYXRpb24ob3V0JEwpDQpgYGANCmBgYHtyIGV2YWw9RkFMU0V9DQojIFJ1biB0aGlzIGlmIHlvdSB3YW50IHRvIHNhdmUgdGhlIG1hdHJpeCBvdXQkUiBpbiB0aGUgZm9sZGVyICJmaWd1cmUiIGludG8gYSAucG5nIGZpbGUNCndyaXRlLmdyYXlJbWFnZShvdXQkUiwnLi9maWd1cmUvZGVlcC1SLnBuZycpDQpgYGANCg0KYGBge3IgZXZhbD1GQUxTRX0NCiMgUnVuIHRoaXMgaWYgeW91IHdhbnQgdG8gc2F2ZSB0aGUgbWF0cml4IG91dCRMIGluIHRoZSBmb2xkZXIgImZpZ3VyZSIgaW50byBhIC5wbmcgZmlsZQ0KcG5nKGZpbGU9Ii4vZmlndXJlL2RlZXAtTC5wbmciLCB3aWR0aD1uY29sKG91dCRMKSwgaGVpZ2h0PShucm93KG91dCRMKSs1MCksIHJlcz0gNzIpDQpwbG90LmdyYXlJbGx1bWluYXRpb24ob3V0JEwpDQpkZXYub2ZmKCkNCmBgYCAgDQogIA0KICANCiAgICANCiAgDQogIA0KICAgDQojIyMjIEhvdyB0byBzZXQgdGhlIG1ldGhvZCBwYXJhbWV0ZXJzPw0KDQoqKkVzdGltYXRpb24gb2YgdGhlIGltYWdlIGdyYWRpZW50IGBgYG1ldGhvZD1gYGAuKiogDQpJZiB0aGUgaW1hZ2UgaXMgbm9pc2UgZnJlZSwgYm90aCBtZXRob2RzIChgYGAiZGlyZWN0ImBgYCBhbmQgYGBgImdhdXNzaWFuImBgYCkgd29yayBhbmQgcHJvZHVjZSBzaW1pbGFyIG91dHB1dHMuIA0KTm90ZSB0aGF0IHRoZSBgYGAiZ2F1c3NpYW4iYGBgIGlzIG1vcmUgdGltZS1jb25zdW1pbmcgYmVjYXVzZSB0aGUgc21vb3RoaW5nIHBhcmFtZXRlciBpcyBjaG9zZW4gYnkgY3Jvc3MtdmFsaWRhdGlvbi4NCklmIHRoZSBpbWFnZSBpcyBub2lzeSwgdGhlIGBgYCJnYXVzc2lhbiJgYGAgbWV0aG9kIHByb3ZpZGVzIHJlc3VsdHMgd2hpY2ggYXJlIG1vcmUgY29uc2lzdGFudCB3aXRoIHRoZSBncm91bmQgdHJ1dGguIA0KVGhlIHVzZXIgbWF5IHNwZWNpZnkgYSBwYWlyIG9mIHBvc2l0aXZlIHZhbHVlcyBmb3IgdGhlIHNtb290aGluZyBwYXJhbWV0ZXIgb2YgdGhlIGxvY2FsIHBvbHlub21pYWwgcHJvY2VkdXJlIHRocm91Z2ggdGhlIG9wdGlvbmFsIGFyZ3VtZW50IGBgYGJhbmR3aWR0aD1gYGAgb2YgdGhlIGZ1bmN0aW9uIGBgYGlsbHVtaW5hdGlvbmBgYCAoaW4gd2hpY2ggY2FzZSwgbm8gY3Jvc3MtdmFsaWRhdGlvbiBwcm9jZWR1cmUgaXMgZG9uZSkuIA0KVGhlIHRyaWNrIGlzIHRoYXQgd2hhdCB3ZSBjYWxsIHRoZSBiYW5kd2lkdGggaXMgYWN0dWFsbHkgdGhlIGludmVyc2Ugb2YgYmFuZHdpZHRoLg0KV2hhdGV2ZXIsIHdlIGFkdmlzZSB0aGUgdW5leHBlcmllbmNlZCB1c2VyIGFnYWluc3QgdGhlIHVzZSBvZiB0aGlzIG9wdGlvbmFsIGFyZ3VtZW50Lg0KYGBge3Igd2FybmluZz1GQUxTRSwgbWVzc2FnZT1GQUxTRSwgZmlnLndpZHRoPTcsIGZpZy5oZWlnaHQ9NiwgZmlnLnNob3c9J2hvbGQnfQ0KIyBSdW4gdGhlIEdLViBlc3RpbWF0aW9uIG1ldGhvZA0Kb3V0X2RpcmVjdCA8LSBpbGx1bWluYXRpb24oZGF0YSwgZGVncmVlPWMoNSw2KSwgbWV0aG9kPSdkaXJlY3QnKQ0Kc3RyKG91dF9kaXJlY3QkcGFycykNCnBsb3QuZ3JheUltYWdlKG91dF9kaXJlY3QkUikNCnBsb3QuZ3JheUlsbHVtaW5hdGlvbihvdXRfZGlyZWN0JEwpDQpgYGANCg0KICANCioqRGVncmVlIG9mIHRoZSBsb2ctaWxsdW1pbmF0aW9uIHBvbHlub21lIGBgYGRlZ3JlZT1gYGAuKiogDQpBcyBvZiBub3csIHdlIGRvIG5vdCBwcm9wb3NlIGFueSBhdXRvbWF0aWMgcHJvY2VkdXJlIHRvIHNlbGVjdCB0aGlzIGFyZ3VtZW50LiANClRoZSB1c2VyIGhhcyB0byB0ZXN0IHNldmVyYWwgcGxhdXNpYmxlIHZhbHVlcyBvZiBwYWlycyBvZiBwb3NpdGl2ZSBpbnRlZ2Vycy4gDQpUbyB0aGlzIGFpbSwgd2UgZm9ybXVsYXRlIHNvbWUgZ3VpZGVsaW5lcyBhY2NvcmRpbmcgdG8gb3VyIGV4cGVyaWVuY2UuDQpGaXJzdCwgd2UgaGlnaGxpZ2h0IHRoYXQgdGhlIGNob2ljZSBmb3IgdGhpcyBwYXJhbWV0ZXIgaXMgdG8gYmUgbWFkZSB3aXRoaW4gYSAoc21hbGwpIGZpbml0ZSBzZXQuIA0KSW5kZWVkLCBpZiB0aGUgZGVncmVlIG9mIHRoZSBwb2x5bm9tZSBpcyBjaG9zZW4gdG9vIGxhcmdlLCB0aGUgbGluZWFyIHN5c3RlbSB0aGF0IGNvbXB1dGVzIHRoZSBwYXJhbWV0ZXIgYGBgZ2FtbWFgYGAgb2Ygb3VyIHByb2NlZHVyZSBpcyBzaW5ndWxhci4NClNlY29uZGx5LCBhbiBpbXBvcnRhbnQgcG9pbnQgaXMgdGhlIGZhY3QgdGhhdCBhIHNpZ25pZmljYW50IG51bWJlciBvZiBwaXhlbCBpbnRlbnNpdHkgbGFyZ2VyIHRoYW4gMSAoYGBgc3VtKG91dCRSPjEpYGBgKSANCkluZGVlZCwgdGhlIHJlZmxlY3RhbmNlIHRvIGJlIHJlY292ZXJlZCBpcyBzdXBwb3NlZCB0byBiZSBpbiBbMCwxXSwgdGhlIGVzdGltYXRlIGlzIHByb3ZlZCB0byBiZSBjb25zaXN0ZW50IGFuZCBubyBwb3N0LXRyZWF0bWVudCBpcyBhcHBsaWVkIG9uIHRoZSByZWZsZWN0YW5jZSBlc3RpbWF0ZSBgYGBvdXQkUmBgYC4gDQpUaHVzLCBpZiB0aGUgZXN0aW1hdGVkIHJlZmxlY3RhbmNlIG1hdHJpeCBnZXRzIGEgc2lnbmlmaWNhbnQgbnVtYmVyIG9mIHBpeGVsIGludGVuc2l0eSBsYXJnZXIgdGhhbiAxIChgYGBzdW0ob3V0JFI+MSlgYGApLCB0aGlzIG1lYW5zIHRoYXQgdGhlIGNob2ljZSBvZiB0aGUgYXJndW1lbnQgYGBgZGVncmVlYGBgIGlzIG5vdCB3ZWxsIHN1aXRlZCB0byB0aGUgaWxsdW1pbmF0aW9uIGFydGVmYWN0IGF0IGhhbmQuDQoNCmBgYHtyIGVjaG89VFJVRX0NCiMgdGhpcyBpcyBhbiBleGFtcGxlIG9mIHRoZSBHS1YgZXN0aW1hdGlvbiBtZXRob2QgcnVuIHdpdGggYW4gaW5jb252ZW5pZW50IGRlZ3JlZSBjaG9pY2UNCm91dF9iYWRfZGVncmVlIDwtIGlsbHVtaW5hdGlvbihkYXRhLCBkZWdyZWU9Yyg1LDgpLCBtZXRob2Q9J2RpcmVjdCcpDQpgYGANCg0KT3VyIHN5c3RlbSBpcyBzZXQgaW4gZnJlbmNoIGhlbmNlIHRoZSBsYW5ndWFnZSBvZiB0aGUgZXJyb3IgbWVzc2FnZS4gDQpUaGlzIGVycm9yIG1lc3NhZ2Ugc2F5cyB0aGF0IHRoZSBsaW5lYXIgc3lzdGVtIHRvIGJlIHNvbHZlZCBpcyBjb21wdXRhdGlvbmFsbHkgc2luZ3VsYXIgYW5kIHRoYXQgaXRzIHJlY2lwcm9jYWwgY29uZGl0aW9uIG51bWJlciBpcyBmYXIgdG9vIHNtYWxsLiAgDQoNCiAgIA0KDQogIA0KICANCioqKg0KPGRpdiBpZD0iZm9vdGVyIj4NCiAgPHA+DQogIEF1dGhvcnM6IDxhIGhyZWY9Imh0dHA6Ly93d3ctaXJtYS51LXN0cmFzYmcuZnIvfmdlZmZyYXkvIiB0YXJnZXQ9Il9ibGFuayI+R2VmZnJheSBTLjwvYT4sIA0KICA8YSBocmVmPSJodHRwczovL3d3dy51bml2LXJlbm5lczIuZnIvaXJtYXItcmVubmVzLTIvbWVtYnJlcyIgdGFyZ2V0PSJfYmxhbmsiPktsdXRjaG5pa29mZiBOLjwvYT4sIA0KICA8YSBocmVmPSJodHRwOi8vd3d3LmVuc2FpLmZyL2Vuc2VpZ25hbnQvYWxpYXMvbXlyaWFtLXZpbW9uZC5odG1sIiB0YXJnZXQ9Il9ibGFuayI+Vmltb25kIE0uPC9hPg0KICA8L3A+ICANCiAgPHA+ICANCiAgTGFzdCBtb2RpZmljYXRpb246IDA5LTA5LTIwMTYNCiAgPC9wPg0KPC9kaXY+DQoNCg0KDQoNCg0KDQoNCg0KDQogDQoNCg0KDQoNCg0KDQoNCg0K