Dirk had a wondelfull idea, to build from scratch a world map out of LEGO (large image). Shout out to Dirk!

I love LEGO, and I love maps even more!

I became obsessed with this ideia. Sure, who wouldn’t?! The problem is the high cost of the endevor… I started looking for a cheaper way from China, but couldn´t find a good source.

Then my third love come to me and said: look at me! I taking about data visualization, in the generic form of R, and in the specific form of a package called Rayshader. Shout out to Tyler Morgan-Wall!

The ideia is simple, to construct my version of this LEGO map in R. It´s fun, not the same fun, but fun nonetheless.

Than before starting I thought, if I am going to do it, let´s do it in the form of a walkthrough, so other people could use this as a guide to repeat the process, or as an introduction to Rayshader. I tried to be as explicid (or verbose) as I could on what I was doing.

The walkthrough is divided in several parts. First we learn how load and read an .png image in order to collect the colors of the map. Then we manipulate the data in order to make a similar map in R. Thirdly we use the functions of rayshader extensively, trying to understand how it works. And finally we apply rayshader in our map, and make a complex gif of the first voyage of Columbus to America.

I wanted to a make something that one could learn something of, the goal of (almost) any data visualization.

1 Image into R

1.1 Load some packages

# install.packages('png') 
# install.packages('ggplot2') 
# install.packages('viridis')
# install.packages("scales")
# install.packages("magick")
# install.packages("devtools")
# devtools::install_github("tylermorganwall/rayshader")
library(png) # image handler
library(ggplot2) # for graphs
library(tidyr) # data wangler
library(viridis) # for color pallete
library(scales) # scales
library(magick) # making gif
library(rayshader) # for all the fun

1.2 Load the image

The image was croped in this online image editor.

Use the function readPNG() of the png package.

#read image
img <- png::readPNG('C:\\Users\\ArthurWelle\\Desktop\\LEGO WORLD MAP\\mapa LEGO 50 X 50.png')

Donwload the image.

By simple manipulation, on MS Paint I know that all the squares are 50 X 50 pixels each. That includes a black line that could take 1 pixel, 3 pixels or more (in order to make a nice grid). If the grid is wider, the square is shorter, but the metrics stays the same, 50px X 50px in total. That´s all we need to know, we just need to take the color of the central point of every square.

If you’re reading a PNG image with alpha channel, then each of the pixel variables is a vector with four entries, each entry corresponding to Red, Green, Blue and Alpha values of the pixel. With no Alpha just the three first colors.

# color of fist pixel
color.top.left <- img[1,1,]     # row 1, column 1
#color of center of fist square
color.center.first.square <- img[25,25,]  # row 25, column 25
color.center.first.square
## [1] 0.03921569 0.41176471 0.68235294

Now, note that the numbers are different of the patterns RGB that we know (in here). That´s because we have a continuum from 0 to 1 representing the more common 1 to 256. If we simple multiply 256 using our numbers above, we have the result we are looking for.

#multiply to conquer!
color.center.first.square<-256*color.center.first.square

#round up and call
color.center.first.square<-trunc(color.center.first.square, 0)
color.center.first.square
## [1]  10 105 174

Cool. Just to be sure that the colors are exactly the same throughout, let´s visit the second square that we know are suppose to be the same color as the first.

#color of center of fist square
color.center.secound.square <- img[25,75,]  # row 25, column 75
color.center.secound.square<-trunc(256*color.center.secound.square) # RGB number and round it
color.center.secound.square
## [1]  10 105 174

Nice! It´s working and the colors are consistent with each other.

2 From image to values

Let´s, then, populate a matrix with the color of every square.

The work of Dirk created the following ranges:

  • Dark Green = land up to 200 meters
  • Light Green = land from 200 to 500 meters
  • Yellow = land from 500 to 1500 meters
  • Light Brown = land from 1500 to 4000 meters
  • Dark Brown = land above 4000 meters
  • Light Blue = water down to 2000 meters
  • Dark Blue = water bellow 2000 meters
  • White = land permanently under ice

Let´s take Alaska and South America as examples for simplicity.

Alaska:

South America:

The Alaska bit above starts at square (9, 1), that is, counting from the top left, 9 squares down, 1 square to the right. The South America bit starts at (56, 48). Both of them have width of 20 squares and height of 16 squares.

So, to get the center pixel of a square, we need to multiply its number/position for 50px, and add a margin (25px).

#create the matrix
chile.matrix<-matrix(data = NA, nrow = 16, ncol = 20)

# populate a matrix
for (i in 0:(nrow(chile.matrix))) { #iterates rows
    for (j in 0:(ncol(chile.matrix))) { #iterates columns
        chile.matrix[i,j] <- paste((trunc(256*(img[(i*50+25 +(51*50) ),(j*50+25+(43*50) ),]))[1]) , #R
                                   (trunc(256*(img[(i*50+25 +(51*50) ),(j*50+25+(43*50) ),]))[2]) , #G
                                   (trunc(256*(img[(i*50+25 +(51*50) ),(j*50+25+(43*50) ),]))[3]) , #B
                                   sep="" )
    }
}
#chile.matrix[]

Sorry for the cumbersome code and output. For debugging purposes I concatenate (paste()) the three colors in one string. My first thought was to use just one of the three as a marker, but than I feared that more than one point would have the same red property.

Let´s show every color:

DarkBlue <-chile.matrix[1,1]
LightBlue <-chile.matrix[1,4]
LightGreen <-chile.matrix[1,7]
DarkGreen <-chile.matrix[1,12]
Yellow <-chile.matrix[2,7]
LightBrown <-chile.matrix[1,6]
DarkBrown <-chile.matrix[2,6]
  • Light Blue = 108152201
  • Dark Blue = 10105174
  • Dark Green = 5013173
  • Light Green = 16518869
  • Yellow = 24420641
  • Light Brown = 16310053
  • Dark Brown = 1056439

2.1 Giving the colors a value

Let´s substitute the RGB colors for single values, since LEGOs are discrete.

We arrive in a moment of decision, we could put values similar to the actual world height, as the legend suggests, or we can put LEGO heights, that is discrete and incremental values, so that when rayshader do its work color represent height not height represents heights. Well, that was a confusing and correct sentence! This is a LEGO world, the choice is obvious.

# colors become numbers
chile.matrix[chile.matrix=="10105174"] <- 1
chile.matrix[chile.matrix=="108152201"] <- 2
chile.matrix[chile.matrix=="16518869"] <- 3
chile.matrix[chile.matrix=="5013173"] <- 4
chile.matrix[chile.matrix=="24420641"] <- 5
chile.matrix[chile.matrix=="16310053"] <- 6
chile.matrix[chile.matrix=="1056439"] <- 7
chile.matrix
##       [,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8] [,9] [,10] [,11] [,12] [,13]
##  [1,] "1"  "1"  "1"  "2"  "6"  "6"  "3"  "3"  "3"  "3"   "3"   "4"   "4"  
##  [2,] "1"  "1"  "1"  "1"  "3"  "7"  "5"  "3"  "3"  "3"   "3"   "3"   "3"  
##  [3,] "1"  "1"  "1"  "1"  "2"  "7"  "6"  "5"  "3"  "3"   "3"   "3"   "3"  
##  [4,] "1"  "1"  "1"  "1"  "1"  "3"  "7"  "6"  "6"  "5"   "3"   "3"   "3"  
##  [5,] "1"  "1"  "1"  "1"  "1"  "2"  "6"  "7"  "7"  "6"   "5"   "3"   "3"  
##  [6,] "1"  "1"  "1"  "1"  "2"  "1"  "1"  "2"  "6"  "7"   "6"   "6"   "3"  
##  [7,] "1"  "1"  "1"  "2"  "1"  "1"  "1"  "1"  "2"  "7"   "7"   "6"   "5"  
##  [8,] "1"  "1"  "2"  "1"  "1"  "1"  "1"  "1"  "2"  "6"   "7"   "6"   "5"  
##  [9,] "1"  "2"  "2"  "1"  "1"  "1"  "1"  "1"  "3"  "6"   "7"   "6"   "3"  
## [10,] "2"  "1"  "2"  "1"  "1"  "1"  "2"  "1"  "3"  "6"   "7"   "5"   "3"  
## [11,] "2"  "2"  "2"  "1"  "2"  "2"  "1"  "1"  "5"  "7"   "6"   "3"   "3"  
## [12,] "2"  "1"  "2"  "1"  "2"  "1"  "1"  "1"  "6"  "7"   "6"   "3"   "4"  
## [13,] "1"  "2"  "1"  "1"  "1"  "1"  "1"  "1"  "6"  "6"   "5"   "3"   "4"  
## [14,] "1"  "2"  "1"  "1"  "1"  "1"  "1"  "1"  "5"  "6"   "5"   "5"   "3"  
## [15,] "1"  "1"  "2"  "2"  "2"  "1"  "1"  "2"  "3"  "6"   "5"   "3"   "3"  
## [16,] "1"  "1"  "1"  "1"  "1"  "1"  "1"  "3"  "6"  "6"   "3"   "3"   "4"  
##       [,14] [,15] [,16] [,17] [,18] [,19] [,20]
##  [1,] "4"   "3"   "3"   "3"   "3"   "5"   "3"  
##  [2,] "3"   "3"   "3"   "3"   "3"   "3"   "3"  
##  [3,] "3"   "3"   "3"   "3"   "3"   "3"   "3"  
##  [4,] "3"   "5"   "5"   "3"   "3"   "3"   "3"  
##  [5,] "3"   "3"   "3"   "5"   "5"   "3"   "5"  
##  [6,] "3"   "3"   "4"   "3"   "5"   "5"   "5"  
##  [7,] "3"   "3"   "3"   "4"   "5"   "5"   "3"  
##  [8,] "3"   "3"   "4"   "3"   "5"   "3"   "3"  
##  [9,] "3"   "3"   "4"   "5"   "3"   "3"   "5"  
## [10,] "3"   "4"   "4"   "3"   "3"   "5"   "5"  
## [11,] "3"   "4"   "4"   "3"   "3"   "5"   "5"  
## [12,] "4"   "4"   "4"   "4"   "3"   "5"   "5"  
## [13,] "4"   "4"   "4"   "3"   "3"   "4"   "2"  
## [14,] "4"   "4"   "4"   "3"   "4"   "2"   "2"  
## [15,] "4"   "4"   "2"   "4"   "2"   "2"   "2"  
## [16,] "4"   "4"   "4"   "2"   "2"   "2"   "1"

2.2 Ploting by color(ggplot2)

Ok, now we should be able to create the plot, using ggplot2 (good reference guide here). We just need a few tweaks in the data structure. We use some functions from tidyverse (fell free to go look it up if you need, I recommend this).

We use the viridis package to make the first color scale, because I like it (the default works fine, and we are going to drop viridis soon).

#transform the matrix in a dataframe... why didnt you started with that in the first place?
d <- as.data.frame(chile.matrix)

#add a column with row names
d$Row <- c(paste(rep("R",16),1:16, sep=""))

# reshape the dataframe, from wide to long format (easyer for ggplot to handle)
d <- tidyr::gather(d, "Hight", "Value", 1:20)

# set the value as numeric, it were factors
d$Value <- as.numeric(d$Value)

#create graph
g<- ggplot2::ggplot(d, aes(y = Row, x = Hight, fill = Value)) +
                     geom_tile()  +
                     scale_fill_viridis_c(option = "C") 
g

Well, that didn´t came out all right…

Ggplot2::geom_tile organized the order of columns and rows with their names. And V10, not V2, follows V1.

Let´s re-do the names of columns and rows, stating in 10.

Another thing, the ggplot start from bottom up, so we have to rewrite the sequence of rows, mirroring it.

Additionally, for aesthetics let´s define de borders between the tiles as black.

# transform to a dataframe
d <- as.data.frame(chile.matrix)

#names in the colums
colnames(d) <- c(paste(rep("V",20),10:29, sep=""))

#add a column with row names, now stating in 25 decreasing to 10
d$Row <- c(paste(rep("R",16),25:10, sep=""))
d <- tidyr::gather(d, "Hight", "Value", 1:20)
d$Value <- as.numeric(d$Value)

#create graph
g<- ggplot2::ggplot(d, aes(y = Row, x = Hight, fill = Value)) +
                    geom_tile(color = "black")  +
                    scale_fill_viridis_c(option = "C") 
g

That´s more like it!

Let´s deal with the colors. We have to make a scale such that the continuum of numbers (heights) show as a discrete list of colors. Note that we have to stick to a continuum in the numbers, rayshader is going to use the values to make the z axis. We just have to change de colors instead. In other words we keep the scale continuous, but with a discrete feeling.

I had to feedle around a bit to find how to do such a trick. The answer (here) comes with the functions scale_fill_gradientn and rescale. Combining the two I can set intervals for my continuous/discrete color scale.

So we are coming back to the colors we got from the image (this time as hex code). That is the circle of life.

#add a column with row names, now stating in 11
g<- ggplot2::ggplot(d, aes(y = Row, x = Hight, fill = Value)) +
                    geom_tile(color = "black")  +
                    scale_fill_gradientn(colours=c(
                      "#6C98C9",
                      "#0A69AE",
                      "#328349", 
                      "#A5BC45",
                      "#F4CE29",
                      "#A36435",
                      "#694027"),
                       values=rescale(c(1,2,3,4,5,6,7)),
                       guide="colorbar")
g

This is going great!

Should we try the full world?

#create the matrix for the whole map
world.matrix<-matrix(data = NA, nrow = 84, ncol = 192)

# populate a matrix
for (i in 0:(nrow(world.matrix)-1)) { #iterates rows
    for (j in 0:(ncol(world.matrix)-1)) { #iterates columns
        world.matrix[i,j] <- paste((trunc(256*(img[(i*50+25 ),(j*50+25 ),]))[1]) , #R
                                   (trunc(256*(img[(i*50+25 ),(j*50+25 ),]))[2]) , #G
                                   (trunc(256*(img[(i*50+25 ),(j*50+25 ),]))[3]) , #B
                                   sep="" )
    }
}

# colors become numbers
world.matrix[world.matrix=="10105174"] <- 1 # for light blue
world.matrix[world.matrix=="108152201"] <- 2 # for dark blue
world.matrix[world.matrix=="5013173"] <- 3 # for light greeb
world.matrix[world.matrix=="253256252"] <- 3 # for white, for now, white would be light green
world.matrix[world.matrix=="16518869"] <- 4 # for dark green
world.matrix[world.matrix=="24420641"] <- 5 # yellow
world.matrix[world.matrix=="16310053"] <- 6 # blow light
world.matrix[world.matrix=="1056439"] <- 7 # brown

# tranform in a data frame, and take last row line and last column line out (they were gray... I dont know, lol). 
d <- as.data.frame(world.matrix[1:83,1:191])

#names in the colums
colnames(d) <- c(paste(rep("V",190),100:289, sep=""))

#add a column with row names, now stating in 11
d$Row <- c(paste(rep("R",83),182:100, sep=""))

# gather
d <- tidyr::gather(d, "Hight", "Value", 1:191)
d$Value <- as.numeric(d$Value)

#create graph
g<- ggplot2::ggplot(d, aes(y = Row, x = Hight, fill = Value)) +
                    geom_tile(color = "gray")  +
                    coord_equal() + # set each tile to be squared, not rectangular
                    labs(x=NULL, y=NULL)+ # take out the axis legends
                    scale_fill_gradientn(colours=c(
                      "#6C98C9", 
                      "#0A69AE",
                      "#A5BC45", 
                      "#328349",
                      "#F4CE29", 
                      "#A36435",
                      "#694027"),
                       values=rescale(c(1,2,3,4,5,6,7)),
                       guide="colorbar") 

# Theme, margins manually edited
g1<- g + theme(    
              axis.ticks=element_blank(), 
              axis.text.x=element_blank(), 
              axis.text.y=element_blank(), 
              axis.title.x=element_blank(), 
              axis.title.y=element_blank(),
              plot.margin = unit(c(0, 0, 0, 0), "cm"), 
              legend.position = 'none') 
g1

It’s a win!

So, a couple of explanations.

I had to look it up how to manually get rid of most of the margins and other features of ggplot in this graph (g + theme above). There are some white tiles in the map. On the center of some of them there is a smaller tile with the color of the altitude (I use this, as I take from each square the color of the central pixel). Still there are some white tiles that don’t have this features, so I decided to set white as light green (world.matrix[world.matrix==“253256252”] <- 3).

Lastly, ggplot2 canvas size is given in the machine output. So I tweak it in the Rmakdown-chunk-options until I got the size that I wanted (fig.height = 4.41, fig.width = 10), with almost no white border.

3 Rayshading the stuff

3.1 Shading LEGO

Now for the cool part. The function of Rayshader plot_gg uses a ggplot object and make it 3d. In our case, a geom_tile, it uses the value used to make the color of the tile to expand it in the z axis. I put the same width and height as the canvas done previuosly, in order to get less white stuff (but it aint perfect yet).

#experiment ray
rayshader::plot_gg(g1, width = 10, height = 4.41, scale = 30, multicore = TRUE, offset_edges=TRUE, windowsize = c(800, 800))

#save the snapshot (if you want it) (or without a name the output comes to Rmakdown).
rayshader::render_snapshot(clear = FALSE)

#do one image, with a set of parameters
rayshader::render_camera(fov= 70, 
                         zoom = 0.1, 
                         theta = 58, 
                         phi = 18)

# take a picture
rayshader::render_snapshot(clear = FALSE)

#one camera picture of the image above (focus)
rayshader::render_depth(focus = 0.72, 
                       focallength = 250, clear = TRUE)

And voilà!! The coast of Africa, with North America as background.

The function rayshader::render_camera makes an view point of the 3d image created above (in the rayshader::plot_gg) The parameters of this function are:

  • theta = Default ‘45’. Rotation angle.
  • phi = Default ‘45’. Azimuth angle. Maximum ‘90’.
  • zoom = Default ‘1’. Positive value indicating camera magnification.
  • fov = Default ‘0’. Field of view of the camera. Maximum ‘180’.

After that we use the rayshader::render_depth function to make a camera photo of the point of view done in the render_camera above. This function has the following parameters (taken from the help):

  • focus = Defaults ‘0.5’. Depth in which to blur. Minimum 0, maximum 1.
  • focallength = Default ‘1’. Focal length of the virtual camera.
  • fstop = Default ‘1’. F-stop of the virtual camera.
  • filename = The filename of the image to be saved. If this is not given, the image will be plotted instead.
  • bokehshape = Default ‘circle’. Also built-in: ‘hex’. The shape of the bokeh.
  • bokehintensity = Default ‘3’. Intensity of the bokeh when the pixel intensity is greater than ‘bokehlimit’.
  • bokehlimit = Default ‘0.8’. Limit after which the bokeh intensity is increased by ‘bokehintensity’.
  • rotation = Default ‘0’. Number of degrees to rotate the hexagon bokeh shape.
  • gamma_correction = Default ‘TRUE’. Controls gamma correction when adding colors. Default exponent of 2.2.
  • transparent_water = Default ‘FALSE’. If ‘TRUE’, depth is determined without water layer. User will have to re-render the water layer with ‘render_water()’ if they want to recreate the water layer.
  • progbar = Default ‘TRUE’ if in an interactive session. Displays a progress bar.
  • clear = Default ‘FALSE’. If ‘TRUE’, the current ‘rgl’ device will be cleared.

I am no photographer and I don´t claim to have understand all these features, but I can appreciate the extend of the awesome work done here! Congratz Tyler!

3.2 Let´s move the camera

Now, with the function rayshader::render_movie we can movement the camera position and attributes doing a movie (mp4, gif). We just play with the parameters phi, theta and zoom. We are going to make three lists for each of these parameters. Each list will have 390 point of view (they have to be the same length).

#parameters for 360 positions
phivechalf = 30 + 60 * 1/(1 + exp(seq(-7, 20, length.out = 180)/2))
phivecfull = c(rep("88.2", 30), phivechalf, rev(phivechalf))
thetavec = c(rep("0", 30), 0 + 60 * sin(seq(0,359,length.out = 360) * pi/180))
zoomvec = 0.25 + 0.4 * 1/(1 + exp(seq(-5, 20, length.out = 180)))
zoomvecfull = c(rep("0.65", 30),zoomvec, rev(zoomvec))

In order to better undestand what is going on, I like to plot these parameters.

#experiment ray
plot(phivecfull)

#experiment ray
plot(thetavec)

#experiment ray
plot(zoomvecfull)

That gives me a sense from where the camera will be looking as the 360 frames progresses. We would like to have smooth transitions in this case, in another we could make cuts in the shot, from one place to the other, it´s up to you. I will save it as a MP4 video. I don´t think that I can embed a video in github pages, so the result is not show here.

# do the dance
rayshader::render_movie(filename="MP4c",
             type='custom',
             frames = 390,  
             phi = phivecfull, 
             zoom = zoomvecfull, 
             theta = thetavec
             )

Ok, done a video of this (but you have to try by yourself). Or you can download the video here:

LINK TO VIDEO.

3.3 Making a steady-cam gif, with focal change.

We did make a photo of the 3d image with focal parameters. As I understood there is no focal parameters in the render_movie type=custom, so we are going to stitch together a bunch of still images in a gif. Let´s do another for loop. In this loop I will start at the 60 and go to 95, these are the focus (focuses) that will be changing over time.

# saving a bunch of imagens with diferent focus
for (i in 60:95) { 
rayshader::render_camera(fov= 70, 
                         zoom = 0.1, 
                         theta = 58, 
                         phi = 18)

rayshader::render_depth(filename=paste("fig",i),
                        focus = i/100, 
                        focallength = 250)
}

And now using magick package make a gif from images.

#list of images, reversed and concatenated
frames <- paste0("fig ", 60:95, ".png")
#frames <-c(frames, rev(frames))

#do the magick
m <- magick::image_read(frames)
m <- magick::image_animate(m)

#save the gif
magick::image_write(m, "movie2.gif")

Due to the limits on gitpages here the

LINK TO GIF.

I guess my wish came true (with some work)! I make myself a nice LEGO map (and github pages likes gifs).

4 Columbus Voyage in a virtual LEGO map.

The only thing that´s left to do is to take things even further! Can we make a gif showing Columbus first voyage trough the Atlantic Ocean?

That will require some work, and again some tricks.

Fist of all, to get another color in the same map, how do we go about it? Add another height? Could be done, but it might be strange. I will interpolate every color/number with a red middle point. So every time we have a 1.5, or a 4.5 in the matrix representing the heights that point will be highlighted in red (with no detriment to the other colors).

Secondly, I need to make the path of the voyage, that is, painfully collecting the points in the map (thanks Ana!).

I will also select only smaller quadrant to show this time.

#create a copy of the data, with no missing, and just a part
d1<-na.omit(d)  %>% 
  dplyr::filter(Row %in% c( paste0("R", 125:175) ))%>%
  dplyr::filter(Hight %in% c( paste0("V", 144:195) ))


#create two verctos with the positions of the travel
lon<-c(  155,154,153,152,151,150,149,148,
         rep(148,31),
         147,147,147,147,147,146,146,146,146,
         147,148,149,150,151,152,153,154,155,156,157,
         rep(157,18))

lat<-c(  185,184,183,182,181,180,179,179,
         178:148,
         147,148,149,150,151,151,152,153,154,
         155,156,157,158,159,160,161,162,163,164,165,
         166:183)

#populate new data, with 0.5 plus in the path above
for (i in 1:(length(lon))) { 
  d1[ d1$Row==paste0("R",lon[i]) & d1$Hight==paste0("V",lat[i]) , ]$Value<-d1[ d1$Row==paste0("R",lon[i]) & d1$Hight==paste0("V",lat[i]) , ]$Value +0.5
}

# take 0.5 of one point that shows in the way in and in the way out
d1[ d1$Row=="R148" & d1$Hight=="V156" , ]$Value<-d1[ d1$Row=="R148" & d1$Hight=="V156" , ]$Value - 0.5

#create graph
g2<- ggplot2::ggplot(d1, aes(y = Row, x = Hight, fill = Value)) +
                    geom_tile(color = "gray")  +
                    coord_equal() + # set each tile to be squared, not rectangular
                    labs(x=NULL, y=NULL)+ # take out the axis legends
                    scale_fill_gradientn(colours=c(
                      "#6C98C9", 
                      "#ff0000", #red
                      "#0A69AE",
                      "#ff0000", #red
                      "#A5BC45",
                      "#ff0000", #red
                      "#328349",
                      "#ff0000", #red
                      "#F4CE29", 
                      "#ff0000", #red
                      "#A36435",
                      "#ff0000", #red
                      "#694027"),
                      #"#ff0000"), #red for some reason could make 7.5, i wont need it
                       values=rescale(c(1,1.5, 2, 2.5, 3, 3.5, 4, 4.5, 5, 5.5, 6, 6.5, 7)),
                       guide="colorbar") 

# Theme, margins manually edited
g2<- g2 + theme(    
              axis.ticks=element_blank(), 
              axis.text.x=element_blank(), 
              axis.text.y=element_blank(), 
              axis.title.x=element_blank(), 
              axis.title.y=element_blank(),
              plot.margin = unit(c(0, 0, 0, 0), "cm"), 
              legend.position = 'none') 
g2

Ok, now it’s time to make one still-frame image of the voyage with rayshader. I will feedle around with the parameters to find good points of view. Then I will record they in my drive as .pngs. That´s a big for loop: construct a new ggplot with the position of Columbus, take the photo and start over.

Before the loop, we soon arrive at a problem. The point of view are a globe around the object. Say you want a photo of that object from other angle, like if you take a step to the side? You be out of the globe of points… So the work around is to change the center of the object, either construction more blank space in some of the sides of the object or you can select a subset of the object to be displayed, this time center at the desired point. Let´s try both of the approaches.

4.0.1 A section of the map

# data without missing and selection only an area
d1<-na.omit(d)%>% 
  dplyr::filter(Row %in% c( paste0("R", 115:182) ))%>% # selection of rows
  dplyr::filter(Hight %in% c( paste0("V", 133:200) )) # selection of columns

# create graph
g2<- ggplot2::ggplot(d1, aes(y = Row, x = Hight, fill = Value)) +
                    geom_tile(color = "gray")  +
                    coord_equal() + # set each tile to be squared, not rectangular
                    labs(x=NULL, y=NULL)+ # take out the axis legends
                    scale_fill_gradientn(colours=c(
                      "#6C98C9", 
                      "#ff0000", #red
                      "#0A69AE",
                      "#ff0000", #red
                      "#A5BC45",
                      "#ff0000", #red
                      "#328349",
                      "#ff0000", #red
                      "#F4CE29", 
                      "#ff0000", #red
                      "#A36435",
                      "#ff0000", #red
                      "#694027"),
                      #"#ff0000"), #red for some reason could make 7.5, i wont need it
                       values=rescale(c(1,1.5, 2, 2.5, 3, 3.5, 4, 4.5, 5, 5.5, 6, 6.5, 7)),
                       guide="colorbar") 

# Theme, margins manually edited
g2<- g2 + theme(    
              axis.ticks=element_blank(), 
              axis.text.x=element_blank(), 
              axis.text.y=element_blank(), 
              axis.title.x=element_blank(), 
              axis.title.y=element_blank(),
              plot.margin = unit(c(0, 0, 0, 0), "cm"), 
              legend.position = 'none') 
g2

# ray
rayshader::plot_gg(g2, width = 6, height = 6, scale = 30, multicore = TRUE, offset_edges=TRUE, windowsize = c(800, 800))

#do one image, with a set of parameters
rayshader::render_camera(fov= 70, 
                         zoom = 0.1, 
                         theta = 100, 
                         phi = 18)

rayshader::render_depth(focus = 0.72, 
                       focallength = 250,
                       clear = TRUE)

4.0.2 Offset map

With the offset map, we got the whole world but in expense to the squared-ness of the bricks being less sharp. I changed plot.margin values.

# data without missing
d1<-na.omit(d)

# create graph
g2<- ggplot2::ggplot(d1, aes(y = Row, x = Hight, fill = Value)) +
                    geom_tile(color = "gray")  +
                    coord_equal() + # set each tile to be squared, not rectangular
                    labs(x=NULL, y=NULL)+ # take out the axis legends
                    scale_fill_gradientn(colours=c(
                      "#6C98C9", 
                      "#ff0000", #red
                      "#0A69AE",
                      "#ff0000", #red
                      "#A5BC45",
                      "#ff0000", #red
                      "#328349",
                      "#ff0000", #red
                      "#F4CE29", 
                      "#ff0000", #red
                      "#A36435",
                      "#ff0000", #red
                      "#694027"),
                      #"#ff0000"), #red for some reason could make 7.5, i wont need it
                       values=rescale(c(1,1.5, 2, 2.5, 3, 3.5, 4, 4.5, 5, 5.5, 6, 6.5, 7)),
                       guide="colorbar") 

# Theme, margins manually edited
g2<- g2 + theme(    
              axis.ticks=element_blank(), 
              axis.text.x=element_blank(), 
              axis.text.y=element_blank(), 
              axis.title.x=element_blank(), 
              axis.title.y=element_blank(),
              plot.margin = unit(c(0, 0, -2.5, 7.5), "cm"), #here add the space
              legend.position = 'none') 
g2

A lot of white space…

# ray
rayshader::plot_gg(g2, width = 12, height = 6, scale = 15, multicore = TRUE, offset_edges=TRUE, windowsize = c(800, 800))

#do one image, with a set of parameters
rayshader::render_camera(fov= 70, 
                         zoom = 0.05, 
                         theta = 100, 
                         phi = 18)

rayshader::render_depth(focus = 0.72, 
                       focallength = 250)

#do one image, with a set of parameters
rayshader::render_camera(fov= 70, 
                         zoom = 0.06, 
                         theta = 285, 
                         phi = 12)

rayshader::render_depth(focus = 0.74, 
                       focallength = 250,
                       clear = TRUE)

I choose de first option, losing some of the map but maintained the sharpness of the tiles.

4.1 The big for loop

We have 77 positions of the boat. Let´s make 77 photos of the object then. We have to start playing with the point of view and construct lists (with 77 elements) for every parameter used.

Let´s plot five point of view:

  • starting the jorney at the coast of Spain
  • coast of Africa
  • arriving at America
  • departure of central America
  • back home
# data without missing
d1<-na.omit(d)%>% 
  dplyr::filter(Row %in% c( paste0("R", 115:182) ))%>% # selection of rows
  dplyr::filter(Hight %in% c( paste0("V", 133:200) )) # selection of columns

# one point of the trip
d1[ d1$Row==paste0("R",lon[i]) & d1$Hight==paste0("V",lat[i]) , ]$Value<-d1[ d1$Row==paste0("R",lon[i]) & d1$Hight==paste0("V",lat[i]) , ]$Value +0.5

# create graph
g2<- ggplot2::ggplot(d1, aes(y = Row, x = Hight, fill = Value)) +
                    geom_tile(color = "gray")  +
                    coord_equal() + # set each tile to be squared, not rectangular
                    labs(x=NULL, y=NULL)+ # take out the axis legends
                    scale_fill_gradientn(colours=c(
                      "#6C98C9", 
                      "#ff0000", #red
                      "#0A69AE",
                      "#ff0000", #red
                      "#A5BC45",
                      "#ff0000", #red
                      "#328349",
                      "#ff0000", #red
                      "#F4CE29", 
                      "#ff0000", #red
                      "#A36435",
                      "#ff0000", #red
                      "#694027"),
                      #"#ff0000"), #red for some reason coulnd make 7.5, I wont need it
                       values=rescale(c(1,1.5, 2, 2.5, 3, 3.5, 4, 4.5, 5, 5.5, 6, 6.5, 7)),
                       guide="colorbar") 

# Theme, margins manually edited
g2<- g2 + theme(    
              axis.ticks=element_blank(), 
              axis.text.x=element_blank(), 
              axis.text.y=element_blank(), 
              axis.title.x=element_blank(), 
              axis.title.y=element_blank(),
              plot.margin = unit(c(0, 0, 0, 0), "cm"), 
              legend.position = 'none') 

# ray
rayshader::plot_gg(g2, width = 6, height = 6, scale = 30, multicore = TRUE, offset_edges=TRUE, windowsize = c(800, 800))

#do one image, with a set of parameters
rayshader::render_camera(fov= 70, 
                         zoom = 0.12, 
                         theta = 100, 
                         phi = 18)

rayshader::render_depth(focus = 0.66, 
                       focallength = 250)

#do one image, with a set of parameters
rayshader::render_camera(fov= 70, 
                         zoom = 0.15, 
                         theta = 95, 
                         phi = 25)

rayshader::render_depth(focus = 0.71, 
                       focallength = 250)

Arriving at land…

#do one image, with a set of parameters
rayshader::render_camera(fov= 70, 
                         zoom = 0.09, 
                         theta =90, 
                         phi = 14)

rayshader::render_depth(focus = 0.88, 
                       focallength = 250)

The return…

#do one image, with a set of parameters
rayshader::render_camera(fov= 70, 
                         zoom = 0.34, 
                         theta =285, 
                         phi = 33)

rayshader::render_depth(focus = 0.70, 
                       focallength = 250)

End point of view

#do one image, with a set of parameters
rayshader::render_camera(fov= 70, 
                         zoom = 0.37, 
                         theta =340, 
                         phi = 57)

rayshader::render_depth(focus = 0.82, 
                       focallength = 250)

Interpolate all the views.

#sequences of parameters (we already have lat lon for columbus position coming from the previos chunk)
List_fov <-         c(rep(70, 77))
List_zoom <-        c(seq(0.12, 0.12, length.out = 10), #10
                      seq(0.12, 0.09, length.out = 30), #20-40
                      seq(0.34, 0.37, length.out = 37)) #40-77
List_theta <-       c(seq(100, 95, length.out = 10), #10
                      seq(95, 90, length.out = 30), #40-77
                      seq(285, 340, length.out = 37)) #40-77
List_phi <-         c(seq(18, 25, length.out = 10), #10
                      seq(25, 14, length.out = 30), #20-40
                      seq(33, 57, length.out = 37)) #40-77
List_focus <-       c(seq(0.66, 0.71, length.out = 10), #10
                      seq(0.71, 0.88, length.out = 30), #20-40
                      seq(0.71, 0.74, length.out = 10), #40-50
                      seq(0.74, 0.81, length.out = 10), #50-60
                      seq(0.81, 0.82, length.out = 17)) #60-77
List_focallength <- c(rep(250, 77))

# data without missing, selected
d1<-na.omit(d)%>% 
  dplyr::filter(Row %in% c( paste0("R", 115:182) ))%>% # selection of rows
  dplyr::filter(Hight %in% c( paste0("V", 133:200) )) # selection of columns

# start the loop
for (i in 1:77) { 

# one point of the trip
d1[ d1$Row==paste0("R",lon[i]) & d1$Hight==paste0("V",lat[i]) , ]$Value<-d1[ d1$Row==paste0("R",lon[i]) & d1$Hight==paste0("V",lat[i]) , ]$Value +0.5

# create graph
g2<- ggplot2::ggplot(d1, aes(y = Row, x = Hight, fill = Value)) +
                    geom_tile(color = "gray")  +
                    coord_equal() + # set each tile to be squared, not rectangular
                    labs(x=NULL, y=NULL)+ # take out the axis legends
                    scale_fill_gradientn(colours=c(
                      "#6C98C9", 
                      "#ff0000", #red
                      "#0A69AE",
                      "#ff0000", #red
                      "#A5BC45",
                      "#ff0000", #red
                      "#328349",
                      "#ff0000", #red
                      "#F4CE29", 
                      "#ff0000", #red
                      "#A36435",
                      "#ff0000", #red
                      "#694027"),
                      #"#ff0000"), #red for some reason coulnd make 7.5, I wont need it
                       values=rescale(c(1,1.5, 2, 2.5, 3, 3.5, 4, 4.5, 5, 5.5, 6, 6.5, 7)),
                       guide="colorbar") 

# Theme, margins manually edited
g2<- g2 + theme(    
              axis.ticks=element_blank(), 
              axis.text.x=element_blank(), 
              axis.text.y=element_blank(), 
              axis.title.x=element_blank(), 
              axis.title.y=element_blank(),
              plot.margin = unit(c(0, 0, 0, 0), "cm"), 
              legend.position = 'none') 

# ray
rayshader::plot_gg(g2, width = 6, height = 6, scale = 30, multicore = TRUE, offset_edges=TRUE, windowsize = c(800, 800))

# make a point of view
rayshader::render_camera(fov= List_fov[i], 
                         zoom = List_zoom[i], 
                         theta = List_theta[i], 
                         phi = List_phi[i])

#save a picture
rayshader::render_depth(filename=paste0("Pic",i),
                        focus = List_focus[i], 
                        focallength = List_focallength[i],
                        clear = TRUE)

# one point out of the trip
d1[ d1$Row==paste0("R",lon[i]) & d1$Hight==paste0("V",lat[i]) , ]$Value<-d1[ d1$Row==paste0("R",lon[i]) & d1$Hight==paste0("V",lat[i]) , ]$Value - (0.5)
}

All together now!

#list of images, reversed and concatenated
frames <- paste0("Pic", 1:77, ".png")
#frames <-c(frames, rev(frames))

#do the magick!
m <- magick::image_read(frames)
m <- magick::image_animate(m, fps = 5)

#save the gif
magick::image_write(m, "Columbus4.gif")

Due to the limits on gitpages here the

LINK TO GIF.

Yep, that it!

4.1.1 A little map on the top of the gif.

Let´s improve the gif with a well behave map, and some information on top. First a little map to be put on top of the video/gif. It´s just a copy of previous work, with small changes.

#create a copy of the data, with no missing, and just a part
d1<-na.omit(d)  %>% 
  dplyr::filter(Row %in% c( paste0("R", 125:175) ))%>%
  dplyr::filter(Hight %in% c( paste0("V", 144:195) ))


#create two verctos with the positions of the travel
lon<-c(  155,154,153,152,151,150,149,148,
         rep(148,31),
         147,147,147,147,147,146,146,146,146,
         147,148,149,150,151,152,153,154,155,156,157,
         rep(157,18))

lat<-c(  185,184,183,182,181,180,179,179,
         178:148,
         147,148,149,150,151,151,152,153,154,
         155,156,157,158,159,160,161,162,163,164,165,
         166:183)


for (i in 1:77) { 
# one point of the trip
d1[ d1$Row==paste0("R",lon[i]) & d1$Hight==paste0("V",lat[i]) , ]$Value<-d1[ d1$Row==paste0("R",lon[i]) & d1$Hight==paste0("V",lat[i]) , ]$Value +0.5

#create graph
g2<- ggplot2::ggplot(d1, aes(y = Row, x = Hight, fill = Value)) +
                    geom_tile()  +
                    coord_equal() + # set each tile to be squared, not rectangular
                    labs(x=NULL, y=NULL)+ # take out the axis legends
                    scale_fill_gradientn(colours=c(
                      "#6C98C9", 
                      "black", #red
                      "#0A69AE",
                      "black", #red
                      "#A5BC45",
                      "black", #red
                      "#328349",
                      "black", #red
                      "#F4CE29", 
                      "black", #red
                      "#A36435",
                      "black", #red
                      "#694027"),
                      #"#ff0000"), #red for some reason coulnd make 7.5, I wont need it
                       values=rescale(c(1,1.5, 2, 2.5, 3, 3.5, 4, 4.5, 5, 5.5, 6, 6.5, 7)),
                       guide="colorbar") 

# Theme, margins manually edited
g2<- g2 + theme(    
              axis.ticks=element_blank(), 
              axis.text.x=element_blank(), 
              axis.text.y=element_blank(), 
              axis.title.x=element_blank(), 
              axis.title.y=element_blank(),
              plot.margin = unit(c(0, 0, 0, 0), "cm"), 
              legend.position = 'none') 

ggplot2::ggsave(filename = paste0("F",i,".png"), plot = g2, width = 2, height = 2, units = "cm" )

}
g2

4.1.2 All the images together on a gif

Let´s make some text to be put together on the gif, the date of the voyage. Additionally let´s put my twitter handle on the bottom of the gif.

Text<-c("03 ago 1492", rep("ago 1492",10), rep("sep 1492",22), rep("out 1492",6),"12 out 1492", rep("out 1492",4), rep("nov 1492",2), rep("dec 1492",1), rep("jan 1493",7), rep("feb 1493",20), rep("mar 1493",2), "04 mar 1493")

for (i in 39:41) { 
  
# all magick manipulation of images
little_pic<-magick::image_read(path = paste0("F",i,".png"))

#take out some unstandard border
little_pic<-magick::image_trim(little_pic)
little_pic<-magick::image_crop(little_pic, "224X224")

#add black border, 2 pixels 
little_pic<-magick::image_border(little_pic, "black", "2X2")

#read the rayshader image
large_pic<-magick::image_read(path = paste0("Pic",i,".png"))

#make a colage from the two images
colage<-magick::image_flatten(c(large_pic, little_pic))

# add some text
colage <- magick::image_annotate(colage, text = Text[i], font = "mono", location = "+8+182", boxcolor = "white", size = 30)   
colage <- magick::image_annotate(colage, text = "Columbus Voyage", font = "times", location = "+4+4", size = 30, boxcolor = "white")  
colage <- magick::image_annotate(colage, text = "@ArthurWelle", font = "mono", location = "+4+708", boxcolor = "white", size = 24)  

#save with another name
magick::image_write(image = colage, path = paste0("T",i,".png"))

}

# Add some text in specific images
T1<-magick::image_read(path = "T40.png")
T1 <- magick::image_annotate(T1, text = "Arrive at Guanahani Island", font = "times", location = "+288+8", size = 40, boxcolor = "white") 
magick::image_write(image = T1, path = "T40.png")

Done! And it’s time to make the final gif.

#list of images, reversed and concatenated
frames <- paste0("T", 1:77, ".png")
#frames <-c(frames, rev(frames))

#do the magick!
m <- magick::image_read(frames)
m <- magick::image_animate(m, fps = 5)

#save the gif
magick::image_write(m, "Columbus10.gif")

Due to the limits on gitpages here the

LINK TO FINAL GIF.

Finish! That´s my take on this issue. Turns out to be a longer take than I expected.

I hope you enjoy the ride/sail as well. There is of course things that can be done better. One that bothered me a bit was the lack of function using/making. Repeating the code when it´s not necessary is a bad practice, but I keep it that way so it would be clearer for any user what’s going on in every part of the code (I could pin point some of code lines that lack clarity! Sorry!). Furthermore I’m still learning R code every day. Don’t take my code at face value.

I learned a lot doing this little side project. Greatest shout out to the guys that did the real base work, Tyler Morgan-Wall and Dirk.

I suppose I must cite here in the end brickr, which is a package for building LEGO bricks in R. I had never tried the package, but I am sure, it´s quite fun as well. Give it a go!

LS0tDQp0aXRsZTogIkxFR08gV09STEQgTUFQIC0gUkFZU0hBREVSIFdBTEtUSFJPVUdIIg0Kb3V0cHV0Og0KICBodG1sX2RvY3VtZW50Og0KICAgIHRvYzogdHJ1ZQ0KICAgIHRvY19mbG9hdDogdHJ1ZQ0KICAgIGNvZGVfZm9sZGluZzogc2hvdw0KICAgIGNvZGVfZG93bmxvYWQ6IHRydWUNCiAgICBudW1iZXJfc2VjdGlvbnM6IHRydWUNCi0tLQ0KPGhlYWQ+DQo8U1RZTEUgVFlQRT0idGV4dC9jc3MiPg0KPCEtLQ0KcCwgdWwgeyANCiAgIGNvbG9yOiBncmV5ODU7IA0KICAgZm9udC1mYW1pbHk6IEdlb3JnaWEsIHNlcmlmOyANCiAgIGZvbnQtc2l6ZTogMTZweDsgDQogICBsaW5lLWhlaWdodDogMjhweDsNCiAgIG1hcmdpbjogMTBweCAxMHB4IDEwcHg7IA0KICAgdGV4dC1pbmRlbnQ6ICA1MHB4Ow0KIH0NCiANCiBwMXsNCiBmb250LXNpemU6IDI2cHg7DQogdGV4dC1pbmRlbnQ6ICAxNTBweDsNCiB9DQotLS0+DQo8L1NUWUxFPg0KPC9oZWFkPg0KDQogIERpcmsgaGFkIGEgd29uZGVsZnVsbCBpZGVhLCB0byBidWlsZCBmcm9tIHNjcmF0Y2ggYSA8YSBocmVmPSJodHRwczovL2RpcmtzLWJsb2cudHVtYmxyLmNvbS9MRUdPLXdvcmxkLW1hcCI+d29ybGQgbWFwIG91dCBvZiBMRUdPPC9hPiAoPGEgaHJlZj0iaHR0cHM6Ly93d3cuZmxpY2tyLmNvbS9waG90b3MvZGlya2I4Ni8zNTk2MjYwODU5MC9pbi9hbGJ1bS03MjE1NzY4Mzc0ODI3MTAzMi8iPmxhcmdlIGltYWdlPC9hPikuIFNob3V0IG91dCB0byAqKjxhIGhyZWY9Imh0dHBzOi8vdHdpdHRlci5jb20vZGlya3NibG9nI189XyI+RGlyayoqPC9hPiENCiAgDQohW10oLi9tYXBhIExFR08gNTAgWCA1MC5wbmcpDQogIA0KICBJIGxvdmUgTEVHTywgYW5kIEkgbG92ZSBtYXBzIGV2ZW4gbW9yZSENCiAgDQogIEkgYmVjYW1lIG9ic2Vzc2VkIHdpdGggdGhpcyBpZGVpYS4gU3VyZSwgd2hvIHdvdWxkbid0PyEgVGhlIHByb2JsZW0gaXMgdGhlIGhpZ2ggY29zdCBvZiB0aGUgZW5kZXZvci4uLiBJIHN0YXJ0ZWQgbG9va2luZyBmb3IgYSBjaGVhcGVyIHdheSBmcm9tIENoaW5hLCBidXQgY291bGRuwrR0IGZpbmQgYSBnb29kIHNvdXJjZS4gDQogIA0KICBUaGVuIG15IHRoaXJkIGxvdmUgY29tZSB0byBtZSBhbmQgc2FpZDogbG9vayBhdCBtZSEgSSB0YWtpbmcgYWJvdXQgZGF0YSB2aXN1YWxpemF0aW9uLCBpbiB0aGUgZ2VuZXJpYyBmb3JtIG9mIFIsIGFuZCBpbiB0aGUgc3BlY2lmaWMgZm9ybSBvZiBhIHBhY2thZ2UgY2FsbGVkICoqPGEgaHJlZj0iaHR0cHM6Ly9naXRodWIuY29tL3R5bGVybW9yZ2Fud2FsbC9yYXlzaGFkZXIiPlJheXNoYWRlcjwvYT4qKi4gU2hvdXQgb3V0IHRvICoqVHlsZXIgTW9yZ2FuLVdhbGwqKiENCiAgDQogIFRoZSBpZGVpYSBpcyBzaW1wbGUsIHRvIGNvbnN0cnVjdCBteSB2ZXJzaW9uIG9mIHRoaXMgTEVHTyBtYXAgaW4gUi4gSXTCtHMgZnVuLCBub3QgdGhlIHNhbWUgZnVuLCBidXQgZnVuIG5vbmV0aGVsZXNzLg0KICANCiAgVGhhbiBiZWZvcmUgc3RhcnRpbmcgSSB0aG91Z2h0LCBpZiBJIGFtIGdvaW5nIHRvIGRvIGl0LCBsZXTCtHMgZG8gaXQgaW4gdGhlIGZvcm0gb2YgYSB3YWxrdGhyb3VnaCwgc28gb3RoZXIgcGVvcGxlIGNvdWxkIHVzZSB0aGlzIGFzIGEgZ3VpZGUgdG8gcmVwZWF0IHRoZSBwcm9jZXNzLCBvciBhcyBhbiBpbnRyb2R1Y3Rpb24gdG8gUmF5c2hhZGVyLiBJIHRyaWVkIHRvIGJlIGFzIGV4cGxpY2lkIChvciB2ZXJib3NlKSBhcyBJIGNvdWxkIG9uIHdoYXQgSSB3YXMgZG9pbmcuDQogIA0KICBUaGUgd2Fsa3Rocm91Z2ggaXMgZGl2aWRlZCBpbiBzZXZlcmFsIHBhcnRzLiBGaXJzdCB3ZSBsZWFybiBob3cgbG9hZCBhbmQgcmVhZCBhbiAqLnBuZyogaW1hZ2UgaW4gb3JkZXIgdG8gY29sbGVjdCB0aGUgY29sb3JzIG9mIHRoZSBtYXAuIFRoZW4gd2UgbWFuaXB1bGF0ZSB0aGUgZGF0YSBpbiBvcmRlciB0byBtYWtlIGEgc2ltaWxhciBtYXAgaW4gUi4gVGhlYXJkbHkgd2UgdXNlIHRoZSBmdW5jdGlvbnMgb2YgcmF5c2hhZGVyIGV4dGVuc2l2ZWx5LCB0cnlpbmcgdG8gdW5kZXJzdGFuZCBob3cgaXQgd29ya3MuIEFuZCBmaW5hbGx5IHdlIGFwcGx5IHJheXNoYWRlciBpbiBvdXIgbWFwLCBhbmQgbWFrZSBhIGNvbXBsZXggZ2lmIG9mIHRoZSBmaXJzdCB2b3lhZ2Ugb2YgQ29sdW1idXMgdG8gQW1lcmljYS4gDQogIA0KICBJIHdhbnRlZCB0byBhIG1ha2Ugc29tZXRoaW5nIHRoYXQgb25lIGNvdWxkIGxlYXJuIHNvbWV0aGluZywgdGhlIGdvYWwgb2YgZGF0YSB2aXN1YWxpemF0aW9uLiANCg0KIyBJbWFnZSBpbnRvIFINCg0KIyMgTG9hZCBzb21lIHBhY2thZ2VzDQoNCmBgYHtyIHNldHVwLCBpbmNsdWRlPVRSVUUsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0UsIGV2YWw9VFJVRSwgZWNobz1UUlVFfQ0KIyBpbnN0YWxsLnBhY2thZ2VzKCdwbmcnKSANCiMgaW5zdGFsbC5wYWNrYWdlcygnZ2dwbG90MicpIA0KIyBpbnN0YWxsLnBhY2thZ2VzKCd2aXJpZGlzJykNCiMgaW5zdGFsbC5wYWNrYWdlcygic2NhbGVzIikNCiMgaW5zdGFsbC5wYWNrYWdlcygibWFnaWNrIikNCiMgaW5zdGFsbC5wYWNrYWdlcygiZGV2dG9vbHMiKQ0KIyBkZXZ0b29sczo6aW5zdGFsbF9naXRodWIoInR5bGVybW9yZ2Fud2FsbC9yYXlzaGFkZXIiKQ0KbGlicmFyeShwbmcpICMgaW1hZ2UgaGFuZGxlcg0KbGlicmFyeShnZ3Bsb3QyKSAjIGZvciBncmFwaHMNCmxpYnJhcnkodGlkeXIpICMgZGF0YSB3YW5nbGVyDQpsaWJyYXJ5KHZpcmlkaXMpICMgZm9yIGNvbG9yIHBhbGxldGUNCmxpYnJhcnkoc2NhbGVzKSAjIHNjYWxlcw0KbGlicmFyeShtYWdpY2spICMgbWFraW5nIGdpZg0KbGlicmFyeShyYXlzaGFkZXIpICMgZm9yIGFsbCB0aGUgZnVuDQpgYGANCg0KIyMgTG9hZCB0aGUgaW1hZ2UNCg0KICBUaGUgaW1hZ2Ugd2FzIGNyb3BlZCBpbiA8YSBocmVmPSJodHRwOi8vd3d3Lm9ubGluZS1pbWFnZS1lZGl0b3IuY29tLyI+IHRoaXMgb25saW5lIGltYWdlIGVkaXRvcjwvYT4uDQogIA0KICBVc2UgdGhlIGZ1bmN0aW9uICpyZWFkUE5HKCkqIG9mIHRoZSAqcG5nKiBwYWNrYWdlLg0KDQpgYGB7ciAsIGluY2x1ZGU9VFJVRSwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRSwgZXZhbD1UUlVFLCBlY2hvPVRSVUV9DQojcmVhZCBpbWFnZQ0KaW1nIDwtIHBuZzo6cmVhZFBORygnQzpcXFVzZXJzXFxBcnRodXJXZWxsZVxcRGVza3RvcFxcTEVHTyBXT1JMRCBNQVBcXG1hcGEgTEVHTyA1MCBYIDUwLnBuZycpDQpgYGANCg0KIEJ5IHNpbXBsZSBtYW5pcHVsYXRpb24sIG9uICpNUyBQYWludCogSSBrbm93IHRoYXQgYWxsIHRoZSBzcXVhcmVzIGFyZSA1MCBYIDUwIHBpeGVscyBlYWNoLiBUaGF0IGluY2x1ZGVzIGEgYmxhY2sgbGluZSB0aGF0IGNvdWxkIHRha2UgMSBwaXhlbCwgMyBwaXhlbHMgb3IgbW9yZSAoaW4gb3JkZXIgdG8gbWFrZSBhIG5pY2UgZ3JpZCkuIElmIHRoZSBncmlkIGlzIHdpZGVyLCB0aGUgc3F1YXJlIGlzIHNob3J0ZXIsIGJ1dCB0aGUgbWV0cmljcyBzdGF5cyB0aGUgc2FtZSwgNTBweCBYIDUwcHggaW4gdG90YWwuIFRoYXTCtHMgYWxsIHdlIG5lZWQgdG8ga25vdywgd2UganVzdCBuZWVkIHRvIHRha2UgdGhlIGNvbG9yIG9mIHRoZSBjZW50cmFsIHBvaW50IG9mIGV2ZXJ5IHNxdWFyZS4NCiANCiBJZiB5b3UncmUgcmVhZGluZyBhIFBORyBpbWFnZSB3aXRoIGFscGhhIGNoYW5uZWwsIHRoZW4gZWFjaCBvZiB0aGUgcGl4ZWwgdmFyaWFibGVzIGlzIGEgdmVjdG9yIHdpdGggZm91ciBlbnRyaWVzLCBlYWNoIGVudHJ5IGNvcnJlc3BvbmRpbmcgdG8gKipSZWQqKiwgKipHcmVlbioqLCAqKkJsdWUqKiBhbmQgKipBbHBoYSoqIHZhbHVlcyBvZiB0aGUgcGl4ZWwuIFdpdGggbm8gQWxwaGEganVzdCB0aGUgdGhyZWUgZmlyc3QgY29sb3JzLg0KIA0KYGBge3IgLCBpbmNsdWRlPVRSVUUsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0UsIGV2YWw9VFJVRSwgZWNobz1UUlVFfQ0KIyBjb2xvciBvZiBmaXN0IHBpeGVsDQpjb2xvci50b3AubGVmdCA8LSBpbWdbMSwxLF0gICAgICMgcm93IDEsIGNvbHVtbiAxDQpgYGANCg0KYGBge3IgLCBpbmNsdWRlPVRSVUUsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0UsIGV2YWw9VFJVRSwgZWNobz1UUlVFfQ0KI2NvbG9yIG9mIGNlbnRlciBvZiBmaXN0IHNxdWFyZQ0KY29sb3IuY2VudGVyLmZpcnN0LnNxdWFyZSA8LSBpbWdbMjUsMjUsXSAgIyByb3cgMjUsIGNvbHVtbiAyNQ0KY29sb3IuY2VudGVyLmZpcnN0LnNxdWFyZQ0KYGBgDQoNCiAgTm93LCBub3RlIHRoYXQgdGhlIG51bWJlcnMgYXJlIGRpZmZlcmVudCBvZiB0aGUgcGF0dGVybnMgUkdCIHRoYXQgd2Uga25vdyAoPGEgaHJlZj0iaHR0cHM6Ly93d3cudzNzY2hvb2xzLmNvbS9jb2xvcnMvY29sb3JzX3BpY2tlci5hc3AiPmluIGhlcmU8L2E+KS4gVGhhdMK0cyBiZWNhdXNlIHdlIGhhdmUgYSBjb250aW51dW0gZnJvbSAwIHRvIDEgcmVwcmVzZW50aW5nIHRoZSBtb3JlIGNvbW1vbiAxIHRvIDI1Ni4gSWYgd2Ugc2ltcGxlIG11bHRpcGx5IDI1NiB1c2luZyBvdXIgbnVtYmVycyBhYm92ZSwgd2UgaGF2ZSB0aGUgcmVzdWx0IHdlIGFyZSBsb29raW5nIGZvci4NCg0KYGBge3IgLCBpbmNsdWRlPVRSVUUsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0UsIGV2YWw9VFJVRSwgZWNobz1UUlVFfQ0KI211bHRpcGx5IHRvIGNvbnF1ZXIhDQpjb2xvci5jZW50ZXIuZmlyc3Quc3F1YXJlPC0yNTYqY29sb3IuY2VudGVyLmZpcnN0LnNxdWFyZQ0KDQojcm91bmQgdXAgYW5kIGNhbGwNCmNvbG9yLmNlbnRlci5maXJzdC5zcXVhcmU8LXRydW5jKGNvbG9yLmNlbnRlci5maXJzdC5zcXVhcmUsIDApDQpjb2xvci5jZW50ZXIuZmlyc3Quc3F1YXJlDQpgYGANCiAgQ29vbC4gSnVzdCB0byBiZSBzdXJlIHRoYXQgdGhlIGNvbG9ycyBhcmUgZXhhY3RseSB0aGUgc2FtZSB0aHJvdWdob3V0LCBsZXTCtHMgdmlzaXQgdGhlIHNlY29uZCBzcXVhcmUgdGhhdCB3ZSBrbm93IGFyZSBzdXBwb3NlIHRvIGJlIHRoZSBzYW1lIGNvbG9yIGFzIHRoZSBmaXJzdC4NCiAgDQpgYGB7ciAsIGluY2x1ZGU9VFJVRSwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRSwgZXZhbD1UUlVFLCBlY2hvPVRSVUV9DQojY29sb3Igb2YgY2VudGVyIG9mIGZpc3Qgc3F1YXJlDQpjb2xvci5jZW50ZXIuc2Vjb3VuZC5zcXVhcmUgPC0gaW1nWzI1LDc1LF0gICMgcm93IDI1LCBjb2x1bW4gNzUNCmNvbG9yLmNlbnRlci5zZWNvdW5kLnNxdWFyZTwtdHJ1bmMoMjU2KmNvbG9yLmNlbnRlci5zZWNvdW5kLnNxdWFyZSkgIyBSR0IgbnVtYmVyIGFuZCByb3VuZCBpdA0KY29sb3IuY2VudGVyLnNlY291bmQuc3F1YXJlDQpgYGANCg0KICBOaWNlISBJdMK0cyB3b3JraW5nIGFuZCB0aGUgY29sb3JzIGFyZSBjb25zaXN0ZW50IHdpdGggZWFjaCBvdGhlci4NCiAgDQojIEZyb20gaW1hZ2UgdG8gdmFsdWVzDQoNCiAgTGV0wrRzLCB0aGFuLCBwb3B1bGF0ZSBhIG1hdHJpeCB3aXRoIHRoZSBjb2xvciBvZiBldmVyeSBzcXVhcmUuDQogIA0KICBUaGUgd29yayBvZiBEaXJrIGNyZWF0ZWQgdGhlIGZvbGxvd2luZyByYW5nZXM6DQogIA0KICAqIERhcmsgR3JlZW4gID0gbGFuZCB1cCB0byAyMDAgbWV0ZXJzDQogICogTGlnaHQgR3JlZW4gPSBsYW5kIGZyb20gMjAwIHRvIDUwMCBtZXRlcnMNCiAgKiBZZWxsb3cgICAgICA9IGxhbmQgZnJvbSA1MDAgdG8gMTUwMCBtZXRlcnMNCiAgKiBMaWdodCBCcm93biA9IGxhbmQgZnJvbSAxNTAwIHRvIDQwMDAgbWV0ZXJzDQogICogRGFyayBCcm93biAgPSBsYW5kIGFib3ZlIDQwMDAgbWV0ZXJzDQogIA0KICAqIExpZ2h0IEJsdWUgID0gd2F0ZXIgZG93biB0byAyMDAwIG1ldGVycyAgDQogICogRGFyayBCbHVlICAgPSB3YXRlciBiZWxsb3cgMjAwMCBtZXRlcnMNCiAgDQogICogd2hpdGUgICAgICAgPSBsYW5kIHBlcm1hbmVudGx5IHVuZGVyIGljZQ0KIA0KIExldMK0cyB0YWtlIEFsYXNrYSBhbmQgU291dGggQW1lcmljYSBhcyBleGFtcGxlcyBmb3Igc2ltcGxpY2l0eS4NCiANCiBBbGFza2E6DQogDQohW10oLi9BbGFza2EucG5nKXsgd2lkdGg9NzUlIH0NCg0KIFNvdXRoIEFtZXJpY2E6DQogDQohW10oLi9DaGlsZS5wbmcgKXsgd2lkdGg9NzUlIH0NCg0KICBUaGUgQWxhc2thIGJpdCBhYm92ZSBzdGFydHMgYXQgc3F1YXJlICg5LCAxKSwgdGhhdCBpcywgY291bnRpbmcgZnJvbSB0aGUgdG9wIGxlZnQsIDkgc3F1YXJlcyBkb3duLCAxIHNxdWFyZSB0byB0aGUgcmlnaHQuIFRoZSBTb3V0aCBBbWVyaWNhIGJpdCBzdGFydHMgYXQgKDU2LCA0OCkuIEJvdGggb2YgdGhlbSBoYXZlIHdpZHRoIG9mIDIwIHNxdWFyZXMgYW5kIGhlaWdodCBvZiAxNiBzcXVhcmVzLiANCiAgDQogIFNvLCB0byBnZXQgdGhlIGNlbnRlciBwaXhlbCBvZiBhIHNxdWFyZSwgd2UgbmVlZCB0byBtdWx0aXBseSBpdHMgbnVtYmVyL3Bvc2l0aW9uIGZvciA1MHB4LCBhbmQgYWRkIGEgbWFyZ2luICgyNXB4KS4NCiANCmBgYHtyICwgaW5jbHVkZT1UUlVFLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFLCBldmFsPVRSVUUsIGVjaG89VFJVRX0NCiNjcmVhdGUgdGhlIG1hdHJpeA0KY2hpbGUubWF0cml4PC1tYXRyaXgoZGF0YSA9IE5BLCBucm93ID0gMTYsIG5jb2wgPSAyMCkNCg0KIyBwb3B1bGF0ZSBhIG1hdHJpeA0KZm9yIChpIGluIDA6KG5yb3coY2hpbGUubWF0cml4KSkpIHsgI2l0ZXJhdGVzIHJvd3MNCiAgICBmb3IgKGogaW4gMDoobmNvbChjaGlsZS5tYXRyaXgpKSkgeyAjaXRlcmF0ZXMgY29sdW1ucw0KICAgICAgICBjaGlsZS5tYXRyaXhbaSxqXSA8LSBwYXN0ZSgodHJ1bmMoMjU2KihpbWdbKGkqNTArMjUgKyg1MSo1MCkgKSwoaio1MCsyNSsoNDMqNTApICksXSkpWzFdKSAsICNSDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICh0cnVuYygyNTYqKGltZ1soaSo1MCsyNSArKDUxKjUwKSApLChqKjUwKzI1Kyg0Myo1MCkgKSxdKSlbMl0pICwgI0cNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKHRydW5jKDI1NiooaW1nWyhpKjUwKzI1ICsoNTEqNTApICksKGoqNTArMjUrKDQzKjUwKSApLF0pKVszXSkgLCAjQg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzZXA9IiIgKQ0KICAgIH0NCn0NCiNjaGlsZS5tYXRyaXhbXQ0KYGBgDQogIA0KICBTb3JyeSBmb3IgdGhlIGN1bWJlcnNvbWUgY29kZSBhbmQgb3V0cHV0LiBGb3IgZGVidWdnaW5nIHB1cnBvc2VzIEkgY29uY2F0ZW5hdGUgKCpwYXN0ZSgpKikgdGhlIHRocmVlIGNvbG9ycyBpbiBvbmUgc3RyaW5nLiBNeSBmaXJzdCB0aG91Z2h0IHdhcyB0byB1c2UganVzdCBvbmUgb2YgdGhlIHRocmVlIGFzIGEgbWFya2VyLCBidXQgdGhhbiBJIGZlYXJlZCB0aGF0IG1vcmUgdGhhbiBvbmUgcG9pbnQgd291bGQgaGF2ZSB0aGUgc2FtZSByZWQgcHJvcGVydHkuIA0KICANCiAgTGV0wrRzIHNob3cgZXZlcnkgY29sb3I6DQogIA0KYGBge3IgLCBpbmNsdWRlPVRSVUUsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0UsIGV2YWw9VFJVRSwgZWNobz1UUlVFfQ0KRGFya0JsdWUgPC1jaGlsZS5tYXRyaXhbMSwxXQ0KTGlnaHRCbHVlIDwtY2hpbGUubWF0cml4WzEsNF0NCkxpZ2h0R3JlZW4gPC1jaGlsZS5tYXRyaXhbMSw3XQ0KRGFya0dyZWVuIDwtY2hpbGUubWF0cml4WzEsMTJdDQpZZWxsb3cgPC1jaGlsZS5tYXRyaXhbMiw3XQ0KTGlnaHRCcm93biA8LWNoaWxlLm1hdHJpeFsxLDZdDQpEYXJrQnJvd24gPC1jaGlsZS5tYXRyaXhbMiw2XQ0KYGBgDQogIA0KICAqIExpZ2h0IEJsdWUgID0gYHIgTGlnaHRCbHVlYA0KICAqIERhcmsgQmx1ZSAgID0gYHIgRGFya0JsdWVgDQogICogRGFyayBHcmVlbiAgPSBgciBEYXJrR3JlZW5gDQogICogTGlnaHQgR3JlZW4gPSBgciBMaWdodEdyZWVuYA0KICAqIFllbGxvdyAgICAgID0gYHIgWWVsbG93YA0KICAqIExpZ2h0IEJyb3duID0gYHIgTGlnaHRCcm93bmANCiAgKiBEYXJrIEJyb3duICA9IGByIERhcmtCcm93bmANCg0KIyMgR2l2aW5nIHRoZSBjb2xvcnMgYSB2YWx1ZQ0KDQogIExldMK0cyBzdWJzdGl0dXRlIHRoZSBSR0IgY29sb3JzIGZvciBzaW5nbGUgdmFsdWVzLCBzaW5jZSBMRUdPcyBhcmUgZGlzY3JldGUuIA0KICANCiAgV2UgYXJyaXZlIGluIGEgbW9tZW50IG9mIGRlY2lzaW9uLCB3ZSBjb3VsZCBwdXQgdmFsdWVzIHNpbWlsYXIgdG8gdGhlIGFjdHVhbCB3b3JsZCBoZWlnaHQsIGFzIHRoZSBsZWdlbmQgc3VnZ2VzdHMsIG9yIHdlIGNhbiBwdXQgTEVHTyBoZWlnaHRzLCB0aGF0IGlzIGRpc2NyZXRlIGFuZCBpbmNyZW1lbnRhbCB2YWx1ZXMsIHNvIHRoYXQgd2hlbiByYXlzaGFkZXIgZG8gaXRzIHdvcmsgY29sb3IgcmVwcmVzZW50IGhlaWdodCBub3QgaGVpZ2h0IHJlcHJlc2VudHMgaGVpZ2h0cy4gV2VsbCwgdGhhdCB3YXMgYSBjb25mdXNpbmcgYW5kIGNvcnJlY3Qgc2VudGVuY2UhIFRoaXMgaXMgYSBMRUdPIHdvcmxkLCB0aGUgY2hvaWNlIGlzIG9idmlvdXMuDQogIA0KYGBge3IgLCBpbmNsdWRlPVRSVUUsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0UsIGV2YWw9VFJVRSwgZWNobz1UUlVFfQ0KIyBjb2xvcnMgYmVjb21lIG51bWJlcnMNCmNoaWxlLm1hdHJpeFtjaGlsZS5tYXRyaXg9PSIxMDEwNTE3NCJdIDwtIDENCmNoaWxlLm1hdHJpeFtjaGlsZS5tYXRyaXg9PSIxMDgxNTIyMDEiXSA8LSAyDQpjaGlsZS5tYXRyaXhbY2hpbGUubWF0cml4PT0iMTY1MTg4NjkiXSA8LSAzDQpjaGlsZS5tYXRyaXhbY2hpbGUubWF0cml4PT0iNTAxMzE3MyJdIDwtIDQNCmNoaWxlLm1hdHJpeFtjaGlsZS5tYXRyaXg9PSIyNDQyMDY0MSJdIDwtIDUNCmNoaWxlLm1hdHJpeFtjaGlsZS5tYXRyaXg9PSIxNjMxMDA1MyJdIDwtIDYNCmNoaWxlLm1hdHJpeFtjaGlsZS5tYXRyaXg9PSIxMDU2NDM5Il0gPC0gNw0KY2hpbGUubWF0cml4DQpgYGANCiAgDQojIyBQbG90aW5nIGJ5IGNvbG9yKCoqZ2dwbG90MioqKQ0KDQogIE9rLCBub3cgd2Ugc2hvdWxkIGJlIGFibGUgdG8gY3JlYXRlIHRoZSBwbG90LCB1c2luZyAqKmdncGxvdDIqKiAoZ29vZCByZWZlcmVuY2UgZ3VpZGUgPGEgaHJlZj0iaHR0cDovL3Itc3RhdGlzdGljcy5jby9Db21wbGV0ZS1HZ3Bsb3QyLVR1dG9yaWFsLVBhcnQxLVdpdGgtUi1Db2RlLmh0bWwiPmhlcmU8L2E+KS4gV2UganVzdCBuZWVkIGEgZmV3IHR3ZWFrcyBpbiB0aGUgZGF0YSBzdHJ1Y3R1cmUuIFdlIHVzZSBzb21lIGZ1bmN0aW9ucyBmcm9tIHRpZHl2ZXJzZSAoZmVsbCBmcmVlIHRvIGdvIGxvb2sgaXQgdXAgaWYgeW91IG5lZWQsIEkgcmVjb21tZW5kIDxhIGhyZWY9Imh0dHBzOi8vd3d3LnJzdHVkaW8uY29tL3dwLWNvbnRlbnQvdXBsb2Fkcy8yMDE1LzAyL2RhdGEtd3JhbmdsaW5nLWNoZWF0c2hlZXQucGRmIj50aGlzPC9hPikuDQogIA0KICBXZSB1c2UgdGhlICoqdmlyaWRpcyoqIHBhY2thZ2UgdG8gbWFrZSB0aGUgZmlyc3QgY29sb3Igc2NhbGUsIGJlY2F1c2UgSSBsaWtlIGl0ICh0aGUgZGVmYXVsdCB3b3JrcyBmaW5lLCBhbmQgd2UgYXJlIGdvaW5nIHRvIGRyb3AgdmlyaWRpcyBzb29uKS4gDQogIA0KYGBge3IgLCBpbmNsdWRlPVRSVUUsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0UsIGV2YWw9VFJVRSwgZWNobz1UUlVFfQ0KI3RyYW5zZm9ybSB0aGUgbWF0cml4IGluIGEgZGF0YWZyYW1lLi4uIHdoeSBkaWRudCB5b3Ugc3RhcnRlZCB3aXRoIHRoYXQgaW4gdGhlIGZpcnN0IHBsYWNlPw0KZCA8LSBhcy5kYXRhLmZyYW1lKGNoaWxlLm1hdHJpeCkNCg0KI2FkZCBhIGNvbHVtbiB3aXRoIHJvdyBuYW1lcw0KZCRSb3cgPC0gYyhwYXN0ZShyZXAoIlIiLDE2KSwxOjE2LCBzZXA9IiIpKQ0KDQojIHJlc2hhcGUgdGhlIGRhdGFmcmFtZSwgZnJvbSB3aWRlIHRvIGxvbmcgZm9ybWF0IChlYXN5ZXIgZm9yIGdncGxvdCB0byBoYW5kbGUpDQpkIDwtIHRpZHlyOjpnYXRoZXIoZCwgIkhpZ2h0IiwgIlZhbHVlIiwgMToyMCkNCg0KIyBzZXQgdGhlIHZhbHVlIGFzIG51bWVyaWMsIGl0IHdlcmUgZmFjdG9ycw0KZCRWYWx1ZSA8LSBhcy5udW1lcmljKGQkVmFsdWUpDQoNCiNjcmVhdGUgZ3JhcGgNCmc8LSBnZ3Bsb3QyOjpnZ3Bsb3QoZCwgYWVzKHkgPSBSb3csIHggPSBIaWdodCwgZmlsbCA9IFZhbHVlKSkgKw0KICAgICAgICAgICAgICAgICAgICAgZ2VvbV90aWxlKCkgICsNCiAgICAgICAgICAgICAgICAgICAgIHNjYWxlX2ZpbGxfdmlyaWRpc19jKG9wdGlvbiA9ICJDIikgDQpnDQpgYGANCg0KICBXZWxsLCB0aGF0IGRpZG7CtHQgY2FtZSBvdXQgYWxsIHJpZ2h0Li4uIA0KICANCiAgKkdncGxvdDI6Omdlb21fdGlsZSogb3JnYW5pemVkIHRoZSBvcmRlciBvZiBjb2x1bW5zIGFuZCByb3dzIHdpdGggdGhlaXIgbmFtZXMuIEFuZCBWMTAsIG5vdCBWMiBmb2xsb3dzIFYxLiANCiAgDQogIExldMK0cyByZS1kbyB0aGUgbmFtZXMgb2YgY29sdW1ucyBhbmQgcm93cywgc3RhdGluZyBpbiAxMC4gDQogIA0KICBBbm90aGVyIHRoaW5nLCB0aGUgZ2dwbG90IHN0YXJ0IGZyb20gYm90dG9tIHVwLCBzbyB3ZSBoYXZlIHRvIHJld3JpdGUgdGhlIHNlcXVlbmNlIG9mIHJvd3MsIG1pcnJvcmluZyBpdC4NCiAgDQogIEFkZGl0aW9uYWxseSwgZm9yIGFlc3RoZXRpY3MgbGV0wrRzIGRlZmluZSBkZSBib3JkZXJzIGJldHdlZW4gdGhlIHRpbGVzIGFzIGJsYWNrLiANCg0KYGBge3IgLCBpbmNsdWRlPVRSVUUsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0UsIGV2YWw9VFJVRSwgZWNobz1UUlVFfQ0KIyB0cmFuc2Zvcm0gdG8gYSBkYXRhZnJhbWUNCmQgPC0gYXMuZGF0YS5mcmFtZShjaGlsZS5tYXRyaXgpDQoNCiNuYW1lcyBpbiB0aGUgY29sdW1zDQpjb2xuYW1lcyhkKSA8LSBjKHBhc3RlKHJlcCgiViIsMjApLDEwOjI5LCBzZXA9IiIpKQ0KDQojYWRkIGEgY29sdW1uIHdpdGggcm93IG5hbWVzLCBub3cgc3RhdGluZyBpbiAyNSBkZWNyZWFzaW5nIHRvIDEwDQpkJFJvdyA8LSBjKHBhc3RlKHJlcCgiUiIsMTYpLDI1OjEwLCBzZXA9IiIpKQ0KZCA8LSB0aWR5cjo6Z2F0aGVyKGQsICJIaWdodCIsICJWYWx1ZSIsIDE6MjApDQpkJFZhbHVlIDwtIGFzLm51bWVyaWMoZCRWYWx1ZSkNCg0KI2NyZWF0ZSBncmFwaA0KZzwtIGdncGxvdDI6OmdncGxvdChkLCBhZXMoeSA9IFJvdywgeCA9IEhpZ2h0LCBmaWxsID0gVmFsdWUpKSArDQogICAgICAgICAgICAgICAgICAgIGdlb21fdGlsZShjb2xvciA9ICJibGFjayIpICArDQogICAgICAgICAgICAgICAgICAgIHNjYWxlX2ZpbGxfdmlyaWRpc19jKG9wdGlvbiA9ICJDIikgDQpnDQoNCmBgYA0KDQogIFRoYXTCtHMgbW9yZSBsaWtlIGl0ISANCiAgDQogIExldMK0cyBkZWFsIHdpdGggdGhlIGNvbG9ycy4gV2UgaGF2ZSB0byBtYWtlIGEgc2NhbGUgc3VjaCB0aGF0IHRoZSBjb250aW51dW0gb2YgbnVtYmVycyAoaGVpZ2h0cykgc2hvdyBhcyBhIGRpc2NyZXRlIGxpc3Qgb2YgY29sb3JzLiBOb3RlIHRoYXQgd2UgaGF2ZSB0byBzdGljayB0byBhIGNvbnRpbnV1bSBpbiB0aGUgbnVtYmVycywgcmF5c2hhZGVyIGlzIGdvaW5nIHRvIHVzZSB0aGUgdmFsdWVzIHRvIG1ha2UgdGhlIHogYXhpcy4gV2UganVzdCBoYXZlIHRvIGNoYW5nZSBkZSBjb2xvcnMgaW5zdGVhZC4gSW4gb3RoZXIgd29yZHMgd2Uga2VlcCB0aGUgc2NhbGUgY29udGludW91cywgYnV0IHdpdGggYSBkaXNjcmV0ZSBmZWVsaW5nLg0KICANCiAgSSBoYWQgdG8gZmVlZGxlIGFyb3VuZCBhIGJpdCB0byBmaW5kIGhvdyB0byBkbyBzdWNoIGEgdHJpY2suIFRoZSBhbnN3ZXIgKDxhIGhyZWY9Imh0dHBzOi8vc3RhY2tvdmVyZmxvdy5jb20vcXVlc3Rpb25zLzEwOTgxMzI0L2dncGxvdDItaGVhdG1hcC13aXRoLWNvbG9ycy1mb3ItcmFuZ2VkLXZhbHVlcyI+aGVyZTwvYT4pIGNvbWVzIHdpdGggdGhlIGZ1bmN0aW9ucyAqKnNjYWxlX2ZpbGxfZ3JhZGllbnRuKiogYW5kIDxhIGhyZWY9Imh0dHBzOi8vd3d3LnJkb2N1bWVudGF0aW9uLm9yZy9wYWNrYWdlcy9zY2FsZXMvdmVyc2lvbnMvMC40LjEvdG9waWNzL3Jlc2NhbGUiPioqcmVzY2FsZSoqPC9hPi4gQ29tYmluaW5nIHRoZSB0d28gSSBjYW4gc2V0IGludGVydmFscyBmb3IgbXkgY29udGludW91cy9kaXNjcmV0ZSBjb2xvciBzY2FsZS4NCiAgDQogIFNvIHdlIGFyZSBjb21pbmcgYmFjayB0byB0aGUgY29sb3JzIHdlIGdvdCBmcm9tIHRoZSBpbWFnZSAodGhpcyB0aW1lIGFzIDxhIGhyZWY9Imh0dHBzOi8vd3d3LnJhcGlkdGFibGVzLmNvbS93ZWIvY29sb3IvUkdCX0NvbG9yLmh0bWwiPmhleCBjb2RlPC9hPikuIFRoYXQgaXMgdGhlIGNpcmNsZSBvZiBsaWZlLg0KDQpgYGB7ciAsIGluY2x1ZGU9VFJVRSwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRSwgZXZhbD1UUlVFLCBlY2hvPVRSVUV9DQojYWRkIGEgY29sdW1uIHdpdGggcm93IG5hbWVzLCBub3cgc3RhdGluZyBpbiAxMQ0KZzwtIGdncGxvdDI6OmdncGxvdChkLCBhZXMoeSA9IFJvdywgeCA9IEhpZ2h0LCBmaWxsID0gVmFsdWUpKSArDQogICAgICAgICAgICAgICAgICAgIGdlb21fdGlsZShjb2xvciA9ICJibGFjayIpICArDQogICAgICAgICAgICAgICAgICAgIHNjYWxlX2ZpbGxfZ3JhZGllbnRuKGNvbG91cnM9YygNCiAgICAgICAgICAgICAgICAgICAgICAiIzZDOThDOSIsDQogICAgICAgICAgICAgICAgICAgICAgIiMwQTY5QUUiLA0KICAgICAgICAgICAgICAgICAgICAgICIjMzI4MzQ5IiwgDQogICAgICAgICAgICAgICAgICAgICAgIiNBNUJDNDUiLA0KICAgICAgICAgICAgICAgICAgICAgICIjRjRDRTI5IiwNCiAgICAgICAgICAgICAgICAgICAgICAiI0EzNjQzNSIsDQogICAgICAgICAgICAgICAgICAgICAgIiM2OTQwMjciKSwNCiAgICAgICAgICAgICAgICAgICAgICAgdmFsdWVzPXJlc2NhbGUoYygxLDIsMyw0LDUsNiw3KSksDQogICAgICAgICAgICAgICAgICAgICAgIGd1aWRlPSJjb2xvcmJhciIpDQpnDQpgYGANCg0KICBUaGlzIGlzIGdvaW5nIGdyZWF0ISANCiAgDQogIFNob3VsZCB3ZSB0cnkgdGhlIGZ1bGwgd29ybGQ/DQoNCmBgYHtyICwgaW5jbHVkZT1UUlVFLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFLCBldmFsPVRSVUUsIGVjaG89VFJVRSwgZmlnLmhlaWdodCA9IDQuNDEsIGZpZy53aWR0aCA9IDEwfQ0KI2NyZWF0ZSB0aGUgbWF0cml4IGZvciB0aGUgd2hvbGUgbWFwDQp3b3JsZC5tYXRyaXg8LW1hdHJpeChkYXRhID0gTkEsIG5yb3cgPSA4NCwgbmNvbCA9IDE5MikNCg0KIyBwb3B1bGF0ZSBhIG1hdHJpeA0KZm9yIChpIGluIDA6KG5yb3cod29ybGQubWF0cml4KS0xKSkgeyAjaXRlcmF0ZXMgcm93cw0KICAgIGZvciAoaiBpbiAwOihuY29sKHdvcmxkLm1hdHJpeCktMSkpIHsgI2l0ZXJhdGVzIGNvbHVtbnMNCiAgICAgICAgd29ybGQubWF0cml4W2ksal0gPC0gcGFzdGUoKHRydW5jKDI1NiooaW1nWyhpKjUwKzI1ICksKGoqNTArMjUgKSxdKSlbMV0pICwgI1INCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKHRydW5jKDI1NiooaW1nWyhpKjUwKzI1ICksKGoqNTArMjUgKSxdKSlbMl0pICwgI0cNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKHRydW5jKDI1NiooaW1nWyhpKjUwKzI1ICksKGoqNTArMjUgKSxdKSlbM10pICwgI0INCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2VwPSIiICkNCiAgICB9DQp9DQoNCiMgY29sb3JzIGJlY29tZSBudW1iZXJzDQp3b3JsZC5tYXRyaXhbd29ybGQubWF0cml4PT0iMTAxMDUxNzQiXSA8LSAxICMgZm9yIGxpZ2h0IGJsdWUNCndvcmxkLm1hdHJpeFt3b3JsZC5tYXRyaXg9PSIxMDgxNTIyMDEiXSA8LSAyICMgZm9yIGRhcmsgYmx1ZQ0Kd29ybGQubWF0cml4W3dvcmxkLm1hdHJpeD09IjUwMTMxNzMiXSA8LSAzICMgZm9yIGxpZ2h0IGdyZWViDQp3b3JsZC5tYXRyaXhbd29ybGQubWF0cml4PT0iMjUzMjU2MjUyIl0gPC0gMyAjIGZvciB3aGl0ZSwgZm9yIG5vdywgd2hpdGUgd291bGQgYmUgbGlnaHQgZ3JlZW4NCndvcmxkLm1hdHJpeFt3b3JsZC5tYXRyaXg9PSIxNjUxODg2OSJdIDwtIDQgIyBmb3IgZGFyayBncmVlbg0Kd29ybGQubWF0cml4W3dvcmxkLm1hdHJpeD09IjI0NDIwNjQxIl0gPC0gNSAjIHllbGxvdw0Kd29ybGQubWF0cml4W3dvcmxkLm1hdHJpeD09IjE2MzEwMDUzIl0gPC0gNiAjIGJsb3cgbGlnaHQNCndvcmxkLm1hdHJpeFt3b3JsZC5tYXRyaXg9PSIxMDU2NDM5Il0gPC0gNyAjIGJyb3duDQoNCiMgdHJhbmZvcm0gaW4gYSBkYXRhIGZyYW1lLCBhbmQgdGFrZSBsYXN0IHJvdyBsaW5lIGFuZCBsYXN0IGNvbHVtbiBsaW5lIG91dCAodGhleSB3ZXJlIGdyYXkuLi4gSSBkb250IGtub3csIGxvbCkuIA0KZCA8LSBhcy5kYXRhLmZyYW1lKHdvcmxkLm1hdHJpeFsxOjgzLDE6MTkxXSkNCg0KI25hbWVzIGluIHRoZSBjb2x1bXMNCmNvbG5hbWVzKGQpIDwtIGMocGFzdGUocmVwKCJWIiwxOTApLDEwMDoyODksIHNlcD0iIikpDQoNCiNhZGQgYSBjb2x1bW4gd2l0aCByb3cgbmFtZXMsIG5vdyBzdGF0aW5nIGluIDExDQpkJFJvdyA8LSBjKHBhc3RlKHJlcCgiUiIsODMpLDE4MjoxMDAsIHNlcD0iIikpDQoNCiMgZ2F0aGVyDQpkIDwtIHRpZHlyOjpnYXRoZXIoZCwgIkhpZ2h0IiwgIlZhbHVlIiwgMToxOTEpDQpkJFZhbHVlIDwtIGFzLm51bWVyaWMoZCRWYWx1ZSkNCg0KI2NyZWF0ZSBncmFwaA0KZzwtIGdncGxvdDI6OmdncGxvdChkLCBhZXMoeSA9IFJvdywgeCA9IEhpZ2h0LCBmaWxsID0gVmFsdWUpKSArDQogICAgICAgICAgICAgICAgICAgIGdlb21fdGlsZShjb2xvciA9ICJncmF5IikgICsNCiAgICAgICAgICAgICAgICAgICAgY29vcmRfZXF1YWwoKSArICMgc2V0IGVhY2ggdGlsZSB0byBiZSBzcXVhcmVkLCBub3QgcmVjdGFuZ3VsYXINCiAgICAgICAgICAgICAgICAgICAgbGFicyh4PU5VTEwsIHk9TlVMTCkrICMgdGFrZSBvdXQgdGhlIGF4aXMgbGVnZW5kcw0KICAgICAgICAgICAgICAgICAgICBzY2FsZV9maWxsX2dyYWRpZW50bihjb2xvdXJzPWMoDQogICAgICAgICAgICAgICAgICAgICAgIiM2Qzk4QzkiLCANCiAgICAgICAgICAgICAgICAgICAgICAiIzBBNjlBRSIsDQogICAgICAgICAgICAgICAgICAgICAgIiNBNUJDNDUiLCANCiAgICAgICAgICAgICAgICAgICAgICAiIzMyODM0OSIsDQogICAgICAgICAgICAgICAgICAgICAgIiNGNENFMjkiLCANCiAgICAgICAgICAgICAgICAgICAgICAiI0EzNjQzNSIsDQogICAgICAgICAgICAgICAgICAgICAgIiM2OTQwMjciKSwNCiAgICAgICAgICAgICAgICAgICAgICAgdmFsdWVzPXJlc2NhbGUoYygxLDIsMyw0LDUsNiw3KSksDQogICAgICAgICAgICAgICAgICAgICAgIGd1aWRlPSJjb2xvcmJhciIpIA0KDQojIFRoZW1lLCBtYXJnaW5zIG1hbnVhbGx5IGVkaXRlZA0KZzE8LSBnICsgdGhlbWUoICAgIA0KICAgICAgICAgICAgICBheGlzLnRpY2tzPWVsZW1lbnRfYmxhbmsoKSwgDQogICAgICAgICAgICAgIGF4aXMudGV4dC54PWVsZW1lbnRfYmxhbmsoKSwgDQogICAgICAgICAgICAgIGF4aXMudGV4dC55PWVsZW1lbnRfYmxhbmsoKSwgDQogICAgICAgICAgICAgIGF4aXMudGl0bGUueD1lbGVtZW50X2JsYW5rKCksIA0KICAgICAgICAgICAgICBheGlzLnRpdGxlLnk9ZWxlbWVudF9ibGFuaygpLA0KICAgICAgICAgICAgICBwbG90Lm1hcmdpbiA9IHVuaXQoYygwLCAwLCAwLCAwKSwgImNtIiksIA0KICAgICAgICAgICAgICBsZWdlbmQucG9zaXRpb24gPSAnbm9uZScpIA0KZzENCmBgYA0KDQogIEl0J3MgYSB3aW4hIA0KICANCiAgU28sIGEgY291cGxlIG9mIGV4cGxhbmF0aW9ucy4gDQogIA0KICBJIGhhZCB0byBsb29rIGl0IHVwIGhvdyB0byBtYW51YWxseSBnZXQgcmlkIG9mIG1vc3Qgb2YgdGhlIG1hcmdpbnMgYW5kIG90aGVyIGZlYXR1cmVzIG9mIGdncGxvdCBpbiB0aGlzIGdyYXBoICgqZyArIHRoZW1lKiBhYm92ZSkuIFRoZXJlIGFyZSBzb21lIHdoaXRlIHRpbGVzIGluIHRoZSBtYXAuIE9uIHRoZSBjZW50ZXIgb2Ygc29tZSBvZiB0aGVtIHRoZXJlIGlzIGEgc21hbGxlciB0aWxlIHdpdGggdGhlIGNvbG9yIG9mIHRoZSBhbHRpdHVkZSAoSSB1c2UgdGhpcywgYXMgSSB0YWtlIGZyb20gZWFjaCBzcXVhcmUgdGhlIGNvbG9yIG9mIHRoZSBjZW50cmFsIHBpeGVsKS4gU3RpbGwgdGhlcmUgYXJlIHNvbWUgd2hpdGUgdGlsZXMgdGhhdCBkb24ndCBoYXZlIHRoaXMgZmVhdHVyZXMsIHNvIEkgZGVjaWRlZCB0byBzZXQgd2hpdGUgYXMgbGlnaHQgZ3JlZW4gKCoqd29ybGQubWF0cml4W3dvcmxkLm1hdHJpeD09IjI1MzI1NjI1MiJdIDwtIDMqKikuIA0KICANCiAgTGFzdGx5LCBnZ3Bsb3QyIGNhbnZhcyBzaXplIGlzIGdpdmVuIGluIHRoZSBtYWNoaW5lIG91dHB1dC4gU28gSSB0d2VhayBpdCBpbiB0aGUgUm1ha2Rvd24tY2h1bmstb3B0aW9ucyB1bnRpbCBJIGdvdCB0aGUgc2l6ZSB0aGF0IEkgd2FudGVkICgqKmZpZy5oZWlnaHQgPSA0LjQxLCBmaWcud2lkdGggPSAxMCoqKSwgd2l0aCBhbG1vc3Qgbm8gd2hpdGUgYm9yZGVyLg0KDQojIFJheXNoYWRpbmcgdGhlIHN0dWZmDQoNCiMjIFNoYWRpbmcgTEVHTw0KDQogIE5vdyBmb3IgdGhlIGNvb2wgcGFydC4gVGhlIGZ1bmN0aW9uIG9mIFJheXNoYWRlciAqKnBsb3RfZ2cqKiB1c2VzIGEgZ2dwbG90IG9iamVjdCBhbmQgbWFrZSBpdCAzZC4gSW4gb3VyIGNhc2UsIGEgZ2VvbV90aWxlLCBpdCB1c2VzIHRoZSB2YWx1ZSB1c2VkIHRvIG1ha2UgdGhlIGNvbG9yIG9mIHRoZSB0aWxlIHRvIGV4cGFuZCBpdCBpbiB0aGUgeiBheGlzLiBJIHB1dCB0aGUgc2FtZSB3aWR0aCBhbmQgaGVpZ2h0IGFzIHRoZSBjYW52YXMgZG9uZSBwcmV2aXVvc2x5LCBpbiBvcmRlciB0byBnZXQgbGVzcyB3aGl0ZSBzdHVmZiAoYnV0IGl0IGFpbnQgcGVyZmVjdCB5ZXQpLg0KIA0KYGBge3IgLCBpbmNsdWRlPVRSVUUsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0UsIGV2YWw9VFJVRSwgZWNobz1UUlVFfQ0KI2V4cGVyaW1lbnQgcmF5DQpyYXlzaGFkZXI6OnBsb3RfZ2coZzEsIHdpZHRoID0gMTAsIGhlaWdodCA9IDQuNDEsIHNjYWxlID0gMzAsIG11bHRpY29yZSA9IFRSVUUsIG9mZnNldF9lZGdlcz1UUlVFLCB3aW5kb3dzaXplID0gYyg4MDAsIDgwMCkpDQoNCiNzYXZlIHRoZSBzbmFwc2hvdCAoaWYgeW91IHdhbnQgaXQpIChvciB3aXRob3V0IGEgbmFtZSB0aGUgb3V0cHV0IGNvbWVzIHRvIFJtYWtkb3duKS4NCnJheXNoYWRlcjo6cmVuZGVyX3NuYXBzaG90KGNsZWFyID0gRkFMU0UpDQpgYGANCg0KYGBge3IgLCBpbmNsdWRlPVRSVUUsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0UsIGV2YWw9VFJVRSwgZWNobz1UUlVFLCBmaWcuaGVpZ2h0ID0gNSwgZmlnLndpZHRoID0gNX0NCiNkbyBvbmUgaW1hZ2UsIHdpdGggYSBzZXQgb2YgcGFyYW1ldGVycw0KcmF5c2hhZGVyOjpyZW5kZXJfY2FtZXJhKGZvdj0gNzAsIA0KICAgICAgICAgICAgICAgICAgICAgICAgIHpvb20gPSAwLjEsIA0KICAgICAgICAgICAgICAgICAgICAgICAgIHRoZXRhID0gNTgsIA0KICAgICAgICAgICAgICAgICAgICAgICAgIHBoaSA9IDE4KQ0KDQojIHRha2UgYSBwaWN0dXJlDQpyYXlzaGFkZXI6OnJlbmRlcl9zbmFwc2hvdChjbGVhciA9IEZBTFNFKQ0KDQpgYGANCg0KYGBge3IgLCBpbmNsdWRlPVRSVUUsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0UsIGV2YWw9VFJVRSwgZWNobz1UUlVFLCBmaWcuaGVpZ2h0ID0gNSwgZmlnLndpZHRoID0gNX0NCiNvbmUgY2FtZXJhIHBpY3R1cmUgb2YgdGhlIGltYWdlIGFib3ZlIChmb2N1cykNCnJheXNoYWRlcjo6cmVuZGVyX2RlcHRoKGZvY3VzID0gMC43MiwgDQogICAgICAgICAgICAgICAgICAgICAgIGZvY2FsbGVuZ3RoID0gMjUwLCBjbGVhciA9IFRSVUUpDQpgYGANCg0KICBBbmQgdm9pbMOgISEgVGhlIGNvYXN0IG9mIEFmcmljYSwgd2l0aCBOb3J0aCBBbWVyaWNhIGFzIGJhY2tncm91bmQuDQogIA0KICBUaGUgZnVuY3Rpb24gKipyYXlzaGFkZXI6OnJlbmRlcl9jYW1lcmEqKiBtYWtlcyBhbiB2aWV3IHBvaW50IG9mIHRoZSAzZCBpbWFnZSBjcmVhdGVkIGFib3ZlIChpbiB0aGUgKnJheXNoYWRlcjo6cGxvdF9nZyopIFRoZSBwYXJhbWV0ZXJzIG9mIHRoaXMgZnVuY3Rpb24gYXJlOg0KICANCiAgKiAqKnRoZXRhKioJID0gRGVmYXVsdCAnNDUnLiBSb3RhdGlvbiBhbmdsZS4NCiAgKiAqKnBoaSoqID0gRGVmYXVsdCAnNDUnLiBBemltdXRoIGFuZ2xlLiBNYXhpbXVtICc5MCcuDQogICogKip6b29tKiogPSBEZWZhdWx0ICcxJy4gUG9zaXRpdmUgdmFsdWUgaW5kaWNhdGluZyBjYW1lcmEgbWFnbmlmaWNhdGlvbi4NCiAgKiAqKmZvdioqID0gRGVmYXVsdCAnMCcuIEZpZWxkIG9mIHZpZXcgb2YgdGhlIGNhbWVyYS4gTWF4aW11bSAnMTgwJy4NCiAgDQogIEFmdGVyIHRoYXQgd2UgdXNlIHRoZSAqKnJheXNoYWRlcjo6cmVuZGVyX2RlcHRoKiogZnVuY3Rpb24gdG8gbWFrZSBhIGNhbWVyYSBwaG90byBvZiB0aGUgcG9pbnQgb2YgdmlldyBkb25lIGluIHRoZSAqcmVuZGVyX2NhbWVyYSogYWJvdmUuIFRoaXMgZnVuY3Rpb24gaGFzIHRoZSBmb2xsb3dpbmcgcGFyYW1ldGVycyAodGFrZW4gZnJvbSB0aGUgaGVscCk6DQogIA0KICAqICoqZm9jdXMqKgk9IERlZmF1bHRzICcwLjUnLiBEZXB0aCBpbiB3aGljaCB0byBibHVyLiBNaW5pbXVtIDAsIG1heGltdW0gMS4NCiAgKiAqKmZvY2FsbGVuZ3RoKioJPSBEZWZhdWx0ICcxJy4gRm9jYWwgbGVuZ3RoIG9mIHRoZSB2aXJ0dWFsIGNhbWVyYS4NCiAgKiAqKmZzdG9wKioJPSBEZWZhdWx0ICcxJy4gRi1zdG9wIG9mIHRoZSB2aXJ0dWFsIGNhbWVyYS4NCiAgKiAqKmZpbGVuYW1lKioJPSBUaGUgZmlsZW5hbWUgb2YgdGhlIGltYWdlIHRvIGJlIHNhdmVkLiBJZiB0aGlzIGlzIG5vdCBnaXZlbiwgdGhlIGltYWdlIHdpbGwgYmUgcGxvdHRlZCBpbnN0ZWFkLg0KICAqICoqYm9rZWhzaGFwZSoqCT0gRGVmYXVsdCAnY2lyY2xlJy4gQWxzbyBidWlsdC1pbjogJ2hleCcuIFRoZSBzaGFwZSBvZiB0aGUgYm9rZWguDQogICogKipib2tlaGludGVuc2l0eSoqCT0gRGVmYXVsdCAnMycuIEludGVuc2l0eSBvZiB0aGUgYm9rZWggd2hlbiB0aGUgcGl4ZWwgaW50ZW5zaXR5IGlzIGdyZWF0ZXIgdGhhbiAnYm9rZWhsaW1pdCcuDQogICogKipib2tlaGxpbWl0KioJPSBEZWZhdWx0ICcwLjgnLiBMaW1pdCBhZnRlciB3aGljaCB0aGUgYm9rZWggaW50ZW5zaXR5IGlzIGluY3JlYXNlZCBieSAnYm9rZWhpbnRlbnNpdHknLg0KICAqICoqcm90YXRpb24qKgk9IERlZmF1bHQgJzAnLiBOdW1iZXIgb2YgZGVncmVlcyB0byByb3RhdGUgdGhlIGhleGFnb24gYm9rZWggc2hhcGUuDQogICogKipnYW1tYV9jb3JyZWN0aW9uKioJPSBEZWZhdWx0ICdUUlVFJy4gQ29udHJvbHMgZ2FtbWEgY29ycmVjdGlvbiB3aGVuIGFkZGluZyBjb2xvcnMuIERlZmF1bHQgZXhwb25lbnQgb2YgMi4yLg0KICAqICoqdHJhbnNwYXJlbnRfd2F0ZXIqKgk9IERlZmF1bHQgJ0ZBTFNFJy4gSWYgJ1RSVUUnLCBkZXB0aCBpcyBkZXRlcm1pbmVkIHdpdGhvdXQgd2F0ZXIgbGF5ZXIuIFVzZXIgd2lsbCBoYXZlIHRvIHJlLXJlbmRlciB0aGUgd2F0ZXIgbGF5ZXIgd2l0aCAncmVuZGVyX3dhdGVyKCknIGlmIHRoZXkgd2FudCB0byByZWNyZWF0ZSB0aGUgd2F0ZXIgbGF5ZXIuDQogICogKipwcm9nYmFyKioJPSBEZWZhdWx0ICdUUlVFJyBpZiBpbiBhbiBpbnRlcmFjdGl2ZSBzZXNzaW9uLiBEaXNwbGF5cyBhIHByb2dyZXNzIGJhci4NCiAgKiAqKmNsZWFyKioJPSBEZWZhdWx0ICdGQUxTRScuIElmICdUUlVFJywgdGhlIGN1cnJlbnQgJ3JnbCcgZGV2aWNlIHdpbGwgYmUgY2xlYXJlZC4NCiAgDQogIEkgYW0gbm8gcGhvdG9ncmFwaGVyIGFuZCBJIGRvbsK0dCBjbGFpbSB0byBoYXZlIHVuZGVyc3RhbmQgYWxsIHRoZXNlIGZlYXR1cmVzLCBidXQgSSBjYW4gYXBwcmVjaWF0ZSB0aGUgZXh0ZW5kIG9mIHRoZSBhd2Vzb21lIHdvcmsgZG9uZSBoZXJlISBDb25ncmF0eiBUeWxlciENCg0KIyMgTGV0wrRzIG1vdmUgdGhlIGNhbWVyYQ0KDQogIE5vdywgd2l0aCB0aGUgZnVuY3Rpb24gKipyYXlzaGFkZXI6OnJlbmRlcl9tb3ZpZSoqIHdlIGNhbiBtb3ZlbWVudCB0aGUgY2FtZXJhIHBvc2l0aW9uIGFuZCBhdHRyaWJ1dGVzIGRvaW5nIGEgbW92aWUgKG1wNCwgZ2lmKS4gV2UganVzdCBwbGF5IHdpdGggdGhlIHBhcmFtZXRlcnMgcGhpLCB0aGV0YSBhbmQgem9vbS4gV2UgYXJlIGdvaW5nIHRvIG1ha2UgdGhyZWUgbGlzdHMgZm9yIGVhY2ggb2YgdGhlc2UgcGFyYW1ldGVycy4gRWFjaCBsaXN0IHdpbGwgaGF2ZSAzOTAgcG9pbnQgb2YgdmlldyAodGhleSBoYXZlIHRvIGJlIHRoZSBzYW1lIGxlbmd0aCkuDQogIA0KYGBge3IgLCBpbmNsdWRlPVRSVUUsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0UsIGV2YWw9VFJVRSwgZWNobz1UUlVFfQ0KI3BhcmFtZXRlcnMgZm9yIDM2MCBwb3NpdGlvbnMNCnBoaXZlY2hhbGYgPSAzMCArIDYwICogMS8oMSArIGV4cChzZXEoLTcsIDIwLCBsZW5ndGgub3V0ID0gMTgwKS8yKSkNCnBoaXZlY2Z1bGwgPSBjKHJlcCgiODguMiIsIDMwKSwgcGhpdmVjaGFsZiwgcmV2KHBoaXZlY2hhbGYpKQ0KdGhldGF2ZWMgPSBjKHJlcCgiMCIsIDMwKSwgMCArIDYwICogc2luKHNlcSgwLDM1OSxsZW5ndGgub3V0ID0gMzYwKSAqIHBpLzE4MCkpDQp6b29tdmVjID0gMC4yNSArIDAuNCAqIDEvKDEgKyBleHAoc2VxKC01LCAyMCwgbGVuZ3RoLm91dCA9IDE4MCkpKQ0Kem9vbXZlY2Z1bGwgPSBjKHJlcCgiMC42NSIsIDMwKSx6b29tdmVjLCByZXYoem9vbXZlYykpDQpgYGANCg0KSW4gb3JkZXIgdG8gYmV0dGVyIHVuZGVzdGFuZCB3aGF0IGlzIGdvaW5nIG9uLCBJIGxpa2UgdG8gcGxvdCB0aGVzZSBwYXJhbWV0ZXJzLiANCg0KYGBge3IgLCBpbmNsdWRlPVRSVUUsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0UsIGV2YWw9VFJVRSwgZWNobz1UUlVFfQ0KI2V4cGVyaW1lbnQgcmF5DQpwbG90KHBoaXZlY2Z1bGwpDQpgYGANCg0KYGBge3IgLCBpbmNsdWRlPVRSVUUsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0UsIGV2YWw9VFJVRSwgZWNobz1UUlVFfQ0KI2V4cGVyaW1lbnQgcmF5DQpwbG90KHRoZXRhdmVjKQ0KYGBgDQoNCmBgYHtyICwgaW5jbHVkZT1UUlVFLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFLCBldmFsPVRSVUUsIGVjaG89VFJVRX0NCiNleHBlcmltZW50IHJheQ0KcGxvdCh6b29tdmVjZnVsbCkNCmBgYA0KDQogIFRoYXQgZ2l2ZXMgbWUgYSBzZW5zZSBmcm9tIHdoZXJlIHRoZSBjYW1lcmEgd2lsbCBiZSBsb29raW5nIGFzIHRoZSAzNjAgZnJhbWVzIHByb2dyZXNzZXMuIFdlIHdvdWxkIGxpa2UgdG8gaGF2ZSBzbW9vdGggdHJhbnNpdGlvbnMgaW4gdGhpcyBjYXNlLCBpbiBhbm90aGVyIHdlIGNvdWxkIG1ha2UgY3V0cyBpbiB0aGUgc2hvdCwgZnJvbSBvbmUgcGxhY2UgdG8gdGhlIG90aGVyLCBpdMK0cyB1cCB0byB5b3UuIEkgd2lsbCBzYXZlIGl0IGFzIGEgTVA0IHZpZGVvLiBJIGRvbsK0dCB0aGluayB0aGF0IEkgY2FuIGVtYmVkIGEgdmlkZW8gaW4gZ2l0aHViIHBhZ2VzLCBzbyB0aGUgcmVzdWx0IGlzIG5vdCBzaG93IGhlcmUuDQoNCmBgYHtyICwgaW5jbHVkZT1UUlVFLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFLCBldmFsPUZBTFNFLCBlY2hvPVRSVUV9DQojIGRvIHRoZSBkYW5jZQ0KcmF5c2hhZGVyOjpyZW5kZXJfbW92aWUoZmlsZW5hbWU9Ik1QNGMiLA0KICAgICAgICAgICAgIHR5cGU9J2N1c3RvbScsDQogICAgICAgICAgICAgZnJhbWVzID0gMzkwLCAgDQogICAgICAgICAgICAgcGhpID0gcGhpdmVjZnVsbCwgDQogICAgICAgICAgICAgem9vbSA9IHpvb212ZWNmdWxsLCANCiAgICAgICAgICAgICB0aGV0YSA9IHRoZXRhdmVjDQogICAgICAgICAgICAgKQ0KYGBgDQoNCiAgT2ssIGRvbmUgYSB2aWRlbyBvZiB0aGlzIChidXQgeW91IGhhdmUgdG8gdHJ5IGJ5IHlvdXJzZWxmKS4NCg0KIyMgTWFraW5nIGEgc3RlYWR5LWNhbSBnaWYsIHdpdGggZm9jYWwgY2hhbmdlLg0KDQogIFdlIGRpZCBtYWtlIGEgcGhvdG8gb2YgdGhlIDNkIGltYWdlIHdpdGggZm9jYWwgcGFyYW1ldGVycy4gQXMgSSB1bmRlcnN0b29kIHRoZXJlIGlzIG5vIGZvY2FsIHBhcmFtZXRlcnMgaW4gdGhlICoqcmVuZGVyX21vdmllIHR5cGU9Y3VzdG9tKiosIHNvIHdlIGFyZSBnb2luZyB0byBzdGl0Y2ggdG9nZXRoZXIgYSBidW5jaCBvZiBzdGlsbCBpbWFnZXMgaW4gYSBnaWYuIExldMK0cyBkbyBhbm90aGVyIGZvciBsb29wLiBJbiB0aGlzIGxvb3AgSSB3aWxsIHN0YXJ0IGF0IHRoZSA2MCBhbmQgZ28gdG8gOTUsIHRoZXNlIGFyZSB0aGUgKmZvY3VzKiAoZm9jdXNlcykgdGhhdCB3aWxsIGJlIGNoYW5naW5nIG92ZXIgdGltZS4gDQoNCmBgYHtyICwgaW5jbHVkZT1UUlVFLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFLCBldmFsPUZBTFNFLCBlY2hvPVRSVUV9DQojIHNhdmluZyBhIGJ1bmNoIG9mIGltYWdlbnMgd2l0aCBkaWZlcmVudCBmb2N1cw0KZm9yIChpIGluIDYwOjk1KSB7IA0KcmF5c2hhZGVyOjpyZW5kZXJfY2FtZXJhKGZvdj0gNzAsIA0KICAgICAgICAgICAgICAgICAgICAgICAgIHpvb20gPSAwLjEsIA0KICAgICAgICAgICAgICAgICAgICAgICAgIHRoZXRhID0gNTgsIA0KICAgICAgICAgICAgICAgICAgICAgICAgIHBoaSA9IDE4KQ0KDQpyYXlzaGFkZXI6OnJlbmRlcl9kZXB0aChmaWxlbmFtZT1wYXN0ZSgiZmlnIixpKSwNCiAgICAgICAgICAgICAgICAgICAgICAgIGZvY3VzID0gaS8xMDAsIA0KICAgICAgICAgICAgICAgICAgICAgICAgZm9jYWxsZW5ndGggPSAyNTApDQp9DQoNCmBgYA0KDQogIEFuZCBub3cgdXNpbmcgKiptYWdpY2sqKiBwYWNrYWdlIG1ha2UgYSBnaWYgZnJvbSBpbWFnZXMuDQoNCmBgYHtyICwgaW5jbHVkZT1UUlVFLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFLCBldmFsPUZBTFNFLCBlY2hvPVRSVUV9DQojbGlzdCBvZiBpbWFnZXMsIHJldmVyc2VkIGFuZCBjb25jYXRlbmF0ZWQNCmZyYW1lcyA8LSBwYXN0ZTAoImZpZyAiLCA2MDo5NSwgIi5wbmciKQ0KI2ZyYW1lcyA8LWMoZnJhbWVzLCByZXYoZnJhbWVzKSkNCg0KI2RvIHRoZSBtYWdpY2sNCm0gPC0gbWFnaWNrOjppbWFnZV9yZWFkKGZyYW1lcykNCm0gPC0gbWFnaWNrOjppbWFnZV9hbmltYXRlKG0pDQoNCiNzYXZlIHRoZSBnaWYNCm1hZ2ljazo6aW1hZ2Vfd3JpdGUobSwgIm1vdmllMi5naWYiKQ0KYGBgDQoNCkR1ZSB0byB0aGUgbGltaXRzIG9uIGdpdHBhZ2VzIGhlcmUgdGhlIA0KDQo8cDE+Kio8YSBocmVmPSJodHRwczovL2dpdGh1Yi5jb20vYXJ0aHVyd2VsbGUvUmF5c2hhZGVyV2Fsa3Rocm91Z2gvYmxvYi9tYXN0ZXIvbW92aWUyLmdpZiI+TElOSyBUTyBHSUY8L2E+KiouPC9wMT4NCg0KICBJIGd1ZXNzIG15IHdpc2ggY2FtZSB0cnVlICh3aXRoIHNvbWUgd29yaykhIEkgbWFrZSBteXNlbGYgYSBuaWNlIExFR08gbWFwIChhbmQgZ2l0aHViIHBhZ2VzIGxpa2VzIGdpZnMpLiANCg0KIyBDb2x1bWJ1cyBWb3lhZ2UgaW4gYSB2aXJ0dWFsIExFR08gbWFwLg0KDQogIFRoZSBvbmx5IHRoaW5nIHRoYXTCtHMgbGVmdCB0byBkbyBpcyB0byB0YWtlIHRoaW5ncyBldmVuIGZ1cnRoZXIhIENhbiB3ZSBtYWtlIGEgZ2lmIHNob3dpbmcgPGEgaHJlZj0iaHR0cHM6Ly9lbi53aWtpcGVkaWEub3JnL3dpa2kvVm95YWdlc19vZl9DaHJpc3RvcGhlcl9Db2x1bWJ1cyMvbWVkaWEvRmlsZTpWaWFqZXNfZGVfY29sb25fZW4uc3ZnIj5Db2x1bWJ1cyBmaXJzdCB2b3lhZ2U8L2E+IHRyb3VnaCB0aGUgQXRsYW50aWMgT2NlYW4/DQogIA0KICBUaGF0IHdpbGwgcmVxdWlyZSBzb21lIHdvcmssIGFuZCBhZ2FpbiBzb21lIHRyaWNrcy4gDQogIA0KICBGaXN0IG9mIGFsbCwgdG8gZ2V0IGFub3RoZXIgY29sb3IgaW4gdGhlIHNhbWUgbWFwLCBob3cgZG8gd2UgZ28gYWJvdXQgaXQ/IEFkZCBhbm90aGVyIGhlaWdodD8gQ291bGQgYmUgZG9uZSwgYnV0IGl0IG1pZ2h0IGJlIHN0cmFuZ2UuIEkgd2lsbCBpbnRlcnBvbGF0ZSBldmVyeSBjb2xvci9udW1iZXIgd2l0aCBhIHJlZCBtaWRkbGUgcG9pbnQuIFNvIGV2ZXJ5IHRpbWUgd2UgaGF2ZSBhIDEuNSwgb3IgYSA0LjUgaW4gdGhlIG1hdHJpeCByZXByZXNlbnRpbmcgdGhlIGhlaWdodHMgdGhhdCBwb2ludCB3aWxsIGJlIGhpZ2hsaWdodGVkIGluIHJlZCAod2l0aCBubyBkZXRyaW1lbnQgdG8gdGhlIG90aGVyIGNvbG9ycykuIA0KICANCiAgU2Vjb25kbHksIEkgbmVlZCB0byBtYWtlIHRoZSBwYXRoIG9mIHRoZSB2b3lhZ2UsIHRoYXQgaXMsIHBhaW5mdWxseSBjb2xsZWN0aW5nIHRoZSBwb2ludHMgaW4gdGhlIG1hcCAodGhhbmtzIEFuYSEpLg0KICANCiAgSSB3aWxsIGFsc28gc2VsZWN0IG9ubHkgc21hbGxlciBxdWFkcmFudCB0byBzaG93IHRoaXMgdGltZS4NCg0KDQpgYGB7ciAsIGluY2x1ZGU9VFJVRSwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRSwgZXZhbD1UUlVFLCBlY2hvPVRSVUUsIGZpZy5oZWlnaHQgPSA2LCBmaWcud2lkdGggPSA2IH0gDQojY3JlYXRlIGEgY29weSBvZiB0aGUgZGF0YSwgd2l0aCBubyBtaXNzaW5nLCBhbmQganVzdCBhIHBhcnQNCmQxPC1uYS5vbWl0KGQpICAlPiUgDQogIGRwbHlyOjpmaWx0ZXIoUm93ICVpbiUgYyggcGFzdGUwKCJSIiwgMTI1OjE3NSkgKSklPiUNCiAgZHBseXI6OmZpbHRlcihIaWdodCAlaW4lIGMoIHBhc3RlMCgiViIsIDE0NDoxOTUpICkpDQoNCg0KI2NyZWF0ZSB0d28gdmVyY3RvcyB3aXRoIHRoZSBwb3NpdGlvbnMgb2YgdGhlIHRyYXZlbA0KbG9uPC1jKCAgMTU1LDE1NCwxNTMsMTUyLDE1MSwxNTAsMTQ5LDE0OCwNCiAgICAgICAgIHJlcCgxNDgsMzEpLA0KICAgICAgICAgMTQ3LDE0NywxNDcsMTQ3LDE0NywxNDYsMTQ2LDE0NiwxNDYsDQogICAgICAgICAxNDcsMTQ4LDE0OSwxNTAsMTUxLDE1MiwxNTMsMTU0LDE1NSwxNTYsMTU3LA0KICAgICAgICAgcmVwKDE1NywxOCkpDQoNCmxhdDwtYyggIDE4NSwxODQsMTgzLDE4MiwxODEsMTgwLDE3OSwxNzksDQogICAgICAgICAxNzg6MTQ4LA0KICAgICAgICAgMTQ3LDE0OCwxNDksMTUwLDE1MSwxNTEsMTUyLDE1MywxNTQsDQogICAgICAgICAxNTUsMTU2LDE1NywxNTgsMTU5LDE2MCwxNjEsMTYyLDE2MywxNjQsMTY1LA0KICAgICAgICAgMTY2OjE4MykNCg0KI3BvcHVsYXRlIG5ldyBkYXRhLCB3aXRoIDAuNSBwbHVzIGluIHRoZSBwYXRoIGFib3ZlDQpmb3IgKGkgaW4gMToobGVuZ3RoKGxvbikpKSB7IA0KICBkMVsgZDEkUm93PT1wYXN0ZTAoIlIiLGxvbltpXSkgJiBkMSRIaWdodD09cGFzdGUwKCJWIixsYXRbaV0pICwgXSRWYWx1ZTwtZDFbIGQxJFJvdz09cGFzdGUwKCJSIixsb25baV0pICYgZDEkSGlnaHQ9PXBhc3RlMCgiViIsbGF0W2ldKSAsIF0kVmFsdWUgKzAuNQ0KfQ0KDQojIHRha2UgMC41IG9mIG9uZSBwb2ludCB0aGF0IHNob3dzIGluIHRoZSB3YXkgaW4gYW5kIGluIHRoZSB3YXkgb3V0DQpkMVsgZDEkUm93PT0iUjE0OCIgJiBkMSRIaWdodD09IlYxNTYiICwgXSRWYWx1ZTwtZDFbIGQxJFJvdz09IlIxNDgiICYgZDEkSGlnaHQ9PSJWMTU2IiAsIF0kVmFsdWUgLSAwLjUNCg0KI2NyZWF0ZSBncmFwaA0KZzI8LSBnZ3Bsb3QyOjpnZ3Bsb3QoZDEsIGFlcyh5ID0gUm93LCB4ID0gSGlnaHQsIGZpbGwgPSBWYWx1ZSkpICsNCiAgICAgICAgICAgICAgICAgICAgZ2VvbV90aWxlKGNvbG9yID0gImdyYXkiKSAgKw0KICAgICAgICAgICAgICAgICAgICBjb29yZF9lcXVhbCgpICsgIyBzZXQgZWFjaCB0aWxlIHRvIGJlIHNxdWFyZWQsIG5vdCByZWN0YW5ndWxhcg0KICAgICAgICAgICAgICAgICAgICBsYWJzKHg9TlVMTCwgeT1OVUxMKSsgIyB0YWtlIG91dCB0aGUgYXhpcyBsZWdlbmRzDQogICAgICAgICAgICAgICAgICAgIHNjYWxlX2ZpbGxfZ3JhZGllbnRuKGNvbG91cnM9YygNCiAgICAgICAgICAgICAgICAgICAgICAiIzZDOThDOSIsIA0KICAgICAgICAgICAgICAgICAgICAgICIjZmYwMDAwIiwgI3JlZA0KICAgICAgICAgICAgICAgICAgICAgICIjMEE2OUFFIiwNCiAgICAgICAgICAgICAgICAgICAgICAiI2ZmMDAwMCIsICNyZWQNCiAgICAgICAgICAgICAgICAgICAgICAiI0E1QkM0NSIsDQogICAgICAgICAgICAgICAgICAgICAgIiNmZjAwMDAiLCAjcmVkDQogICAgICAgICAgICAgICAgICAgICAgIiMzMjgzNDkiLA0KICAgICAgICAgICAgICAgICAgICAgICIjZmYwMDAwIiwgI3JlZA0KICAgICAgICAgICAgICAgICAgICAgICIjRjRDRTI5IiwgDQogICAgICAgICAgICAgICAgICAgICAgIiNmZjAwMDAiLCAjcmVkDQogICAgICAgICAgICAgICAgICAgICAgIiNBMzY0MzUiLA0KICAgICAgICAgICAgICAgICAgICAgICIjZmYwMDAwIiwgI3JlZA0KICAgICAgICAgICAgICAgICAgICAgICIjNjk0MDI3IiksDQogICAgICAgICAgICAgICAgICAgICAgIyIjZmYwMDAwIiksICNyZWQgZm9yIHNvbWUgcmVhc29uIGNvdWxkIG1ha2UgNy41LCBpIHdvbnQgbmVlZCBpdA0KICAgICAgICAgICAgICAgICAgICAgICB2YWx1ZXM9cmVzY2FsZShjKDEsMS41LCAyLCAyLjUsIDMsIDMuNSwgNCwgNC41LCA1LCA1LjUsIDYsIDYuNSwgNykpLA0KICAgICAgICAgICAgICAgICAgICAgICBndWlkZT0iY29sb3JiYXIiKSANCg0KIyBUaGVtZSwgbWFyZ2lucyBtYW51YWxseSBlZGl0ZWQNCmcyPC0gZzIgKyB0aGVtZSggICAgDQogICAgICAgICAgICAgIGF4aXMudGlja3M9ZWxlbWVudF9ibGFuaygpLCANCiAgICAgICAgICAgICAgYXhpcy50ZXh0Lng9ZWxlbWVudF9ibGFuaygpLCANCiAgICAgICAgICAgICAgYXhpcy50ZXh0Lnk9ZWxlbWVudF9ibGFuaygpLCANCiAgICAgICAgICAgICAgYXhpcy50aXRsZS54PWVsZW1lbnRfYmxhbmsoKSwgDQogICAgICAgICAgICAgIGF4aXMudGl0bGUueT1lbGVtZW50X2JsYW5rKCksDQogICAgICAgICAgICAgIHBsb3QubWFyZ2luID0gdW5pdChjKDAsIDAsIDAsIDApLCAiY20iKSwgDQogICAgICAgICAgICAgIGxlZ2VuZC5wb3NpdGlvbiA9ICdub25lJykgDQpnMg0KYGBgDQoNCiAgT2ssIG5vdyBpdCdzIHRpbWUgdG8gbWFrZSBvbmUgc3RpbGwtZnJhbWUgaW1hZ2Ugb2YgdGhlIHZveWFnZSB3aXRoIHJheXNoYWRlci4gSSB3aWxsIGZlZWRsZSBhcm91bmQgd2l0aCB0aGUgcGFyYW1ldGVycyB0byBmaW5kIGdvb2QgcG9pbnRzIG9mIHZpZXcuIFRoZW4gSSB3aWxsIHJlY29yZCB0aGV5IGluIG15IGRyaXZlIGFzIC5wbmdzLiBUaGF0wrRzIGEgYmlnICpmb3IgbG9vcCo6IGNvbnN0cnVjdCBhIG5ldyBnZ3Bsb3Qgd2l0aCB0aGUgcG9zaXRpb24gb2YgQ29sdW1idXMsIHRha2UgdGhlIHBob3RvIGFuZCBzdGFydCBvdmVyLiANCiAgDQogIEJlZm9yZSB0aGUgbG9vcCwgd2Ugc29vbiBhcnJpdmUgYXQgYSBwcm9ibGVtLiBUaGUgcG9pbnQgb2YgdmlldyBhcmUgYSBnbG9iZSBhcm91bmQgdGhlIG9iamVjdC4gU2F5IHlvdSB3YW50IGEgcGhvdG8gb2YgdGhhdCBvYmplY3QgZnJvbSBvdGhlciBhbmdsZSwgbGlrZSBpZiB5b3UgdGFrZSBhIHN0ZXAgdG8gdGhlIHNpZGU/IFlvdSBiZSBvdXQgb2YgdGhlIGdsb2JlIG9mIHBvaW50cy4uLiBTbyB0aGUgd29yayBhcm91bmQgaXMgdG8gY2hhbmdlIHRoZSBjZW50ZXIgb2YgdGhlIG9iamVjdCwgZWl0aGVyIGNvbnN0cnVjdGlvbiBtb3JlIGJsYW5rIHNwYWNlIGluIHNvbWUgb2YgdGhlIHNpZGVzIG9mIHRoZSBvYmplY3Qgb3IgeW91IGNhbiBzZWxlY3QgYSBzdWJzZXQgb2YgdGhlIG9iamVjdCB0byBiZSBkaXNwbGF5ZWQsIHRoaXMgdGltZSBjZW50ZXIgYXQgdGhlIGRlc2lyZWQgcG9pbnQuIExldMK0cyB0cnkgYm90aCBvZiB0aGUgYXBwcm9hY2hlcy4NCiAgDQojIyMgQSBzZWN0aW9uIG9mIHRoZSBtYXANCiAgDQpgYGB7ciAsIGluY2x1ZGU9VFJVRSwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRSwgZXZhbD1UUlVFLCBlY2hvPVRSVUV9IA0KIyBkYXRhIHdpdGhvdXQgbWlzc2luZyBhbmQgc2VsZWN0aW9uIG9ubHkgYW4gYXJlYQ0KZDE8LW5hLm9taXQoZCklPiUgDQogIGRwbHlyOjpmaWx0ZXIoUm93ICVpbiUgYyggcGFzdGUwKCJSIiwgMTE1OjE4MikgKSklPiUgIyBzZWxlY3Rpb24gb2Ygcm93cw0KICBkcGx5cjo6ZmlsdGVyKEhpZ2h0ICVpbiUgYyggcGFzdGUwKCJWIiwgMTMzOjIwMCkgKSkgIyBzZWxlY3Rpb24gb2YgY29sdW1ucw0KDQojIGNyZWF0ZSBncmFwaA0KZzI8LSBnZ3Bsb3QyOjpnZ3Bsb3QoZDEsIGFlcyh5ID0gUm93LCB4ID0gSGlnaHQsIGZpbGwgPSBWYWx1ZSkpICsNCiAgICAgICAgICAgICAgICAgICAgZ2VvbV90aWxlKGNvbG9yID0gImdyYXkiKSAgKw0KICAgICAgICAgICAgICAgICAgICBjb29yZF9lcXVhbCgpICsgIyBzZXQgZWFjaCB0aWxlIHRvIGJlIHNxdWFyZWQsIG5vdCByZWN0YW5ndWxhcg0KICAgICAgICAgICAgICAgICAgICBsYWJzKHg9TlVMTCwgeT1OVUxMKSsgIyB0YWtlIG91dCB0aGUgYXhpcyBsZWdlbmRzDQogICAgICAgICAgICAgICAgICAgIHNjYWxlX2ZpbGxfZ3JhZGllbnRuKGNvbG91cnM9YygNCiAgICAgICAgICAgICAgICAgICAgICAiIzZDOThDOSIsIA0KICAgICAgICAgICAgICAgICAgICAgICIjZmYwMDAwIiwgI3JlZA0KICAgICAgICAgICAgICAgICAgICAgICIjMEE2OUFFIiwNCiAgICAgICAgICAgICAgICAgICAgICAiI2ZmMDAwMCIsICNyZWQNCiAgICAgICAgICAgICAgICAgICAgICAiI0E1QkM0NSIsDQogICAgICAgICAgICAgICAgICAgICAgIiNmZjAwMDAiLCAjcmVkDQogICAgICAgICAgICAgICAgICAgICAgIiMzMjgzNDkiLA0KICAgICAgICAgICAgICAgICAgICAgICIjZmYwMDAwIiwgI3JlZA0KICAgICAgICAgICAgICAgICAgICAgICIjRjRDRTI5IiwgDQogICAgICAgICAgICAgICAgICAgICAgIiNmZjAwMDAiLCAjcmVkDQogICAgICAgICAgICAgICAgICAgICAgIiNBMzY0MzUiLA0KICAgICAgICAgICAgICAgICAgICAgICIjZmYwMDAwIiwgI3JlZA0KICAgICAgICAgICAgICAgICAgICAgICIjNjk0MDI3IiksDQogICAgICAgICAgICAgICAgICAgICAgIyIjZmYwMDAwIiksICNyZWQgZm9yIHNvbWUgcmVhc29uIGNvdWxkIG1ha2UgNy41LCBpIHdvbnQgbmVlZCBpdA0KICAgICAgICAgICAgICAgICAgICAgICB2YWx1ZXM9cmVzY2FsZShjKDEsMS41LCAyLCAyLjUsIDMsIDMuNSwgNCwgNC41LCA1LCA1LjUsIDYsIDYuNSwgNykpLA0KICAgICAgICAgICAgICAgICAgICAgICBndWlkZT0iY29sb3JiYXIiKSANCg0KIyBUaGVtZSwgbWFyZ2lucyBtYW51YWxseSBlZGl0ZWQNCmcyPC0gZzIgKyB0aGVtZSggICAgDQogICAgICAgICAgICAgIGF4aXMudGlja3M9ZWxlbWVudF9ibGFuaygpLCANCiAgICAgICAgICAgICAgYXhpcy50ZXh0Lng9ZWxlbWVudF9ibGFuaygpLCANCiAgICAgICAgICAgICAgYXhpcy50ZXh0Lnk9ZWxlbWVudF9ibGFuaygpLCANCiAgICAgICAgICAgICAgYXhpcy50aXRsZS54PWVsZW1lbnRfYmxhbmsoKSwgDQogICAgICAgICAgICAgIGF4aXMudGl0bGUueT1lbGVtZW50X2JsYW5rKCksDQogICAgICAgICAgICAgIHBsb3QubWFyZ2luID0gdW5pdChjKDAsIDAsIDAsIDApLCAiY20iKSwgDQogICAgICAgICAgICAgIGxlZ2VuZC5wb3NpdGlvbiA9ICdub25lJykgDQpnMg0KYGBgDQoNCmBgYHtyICwgaW5jbHVkZT1UUlVFLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFLCBldmFsPVRSVUUsIGVjaG89VFJVRSwgZmlnLmhlaWdodCA9IDYsIGZpZy53aWR0aCA9IDZ9IA0KIyByYXkNCnJheXNoYWRlcjo6cGxvdF9nZyhnMiwgd2lkdGggPSA2LCBoZWlnaHQgPSA2LCBzY2FsZSA9IDMwLCBtdWx0aWNvcmUgPSBUUlVFLCBvZmZzZXRfZWRnZXM9VFJVRSwgd2luZG93c2l6ZSA9IGMoODAwLCA4MDApKQ0KDQojZG8gb25lIGltYWdlLCB3aXRoIGEgc2V0IG9mIHBhcmFtZXRlcnMNCnJheXNoYWRlcjo6cmVuZGVyX2NhbWVyYShmb3Y9IDcwLCANCiAgICAgICAgICAgICAgICAgICAgICAgICB6b29tID0gMC4xLCANCiAgICAgICAgICAgICAgICAgICAgICAgICB0aGV0YSA9IDEwMCwgDQogICAgICAgICAgICAgICAgICAgICAgICAgcGhpID0gMTgpDQoNCnJheXNoYWRlcjo6cmVuZGVyX2RlcHRoKGZvY3VzID0gMC43MiwgDQogICAgICAgICAgICAgICAgICAgICAgIGZvY2FsbGVuZ3RoID0gMjUwLA0KICAgICAgICAgICAgICAgICAgICAgICBjbGVhciA9IFRSVUUpDQpgYGANCg0KIyMjIE9mZnNldCBtYXANCg0KICBXaXRoIHRoZSBvZmZzZXQgbWFwLCB3ZSBnb3QgdGhlIHdob2xlIHdvcmxkIGJ1dCBpbiBleHBlbnNlIHRvIHRoZSBzcXVhcmVkLW5lc3Mgb2YgdGhlIGJyaWNrcyBiZWluZyBsZXNzIHNoYXJwLiBJIGNoYW5nZWQgKnBsb3QubWFyZ2luKiB2YWx1ZXMuDQoNCmBgYHtyICwgaW5jbHVkZT1UUlVFLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFLCBldmFsPVRSVUUsIGVjaG89VFJVRSwgZmlnLmhlaWdodCA9IDYsIGZpZy53aWR0aCA9IDEyfQ0KIyBkYXRhIHdpdGhvdXQgbWlzc2luZw0KZDE8LW5hLm9taXQoZCkNCg0KIyBjcmVhdGUgZ3JhcGgNCmcyPC0gZ2dwbG90Mjo6Z2dwbG90KGQxLCBhZXMoeSA9IFJvdywgeCA9IEhpZ2h0LCBmaWxsID0gVmFsdWUpKSArDQogICAgICAgICAgICAgICAgICAgIGdlb21fdGlsZShjb2xvciA9ICJncmF5IikgICsNCiAgICAgICAgICAgICAgICAgICAgY29vcmRfZXF1YWwoKSArICMgc2V0IGVhY2ggdGlsZSB0byBiZSBzcXVhcmVkLCBub3QgcmVjdGFuZ3VsYXINCiAgICAgICAgICAgICAgICAgICAgbGFicyh4PU5VTEwsIHk9TlVMTCkrICMgdGFrZSBvdXQgdGhlIGF4aXMgbGVnZW5kcw0KICAgICAgICAgICAgICAgICAgICBzY2FsZV9maWxsX2dyYWRpZW50bihjb2xvdXJzPWMoDQogICAgICAgICAgICAgICAgICAgICAgIiM2Qzk4QzkiLCANCiAgICAgICAgICAgICAgICAgICAgICAiI2ZmMDAwMCIsICNyZWQNCiAgICAgICAgICAgICAgICAgICAgICAiIzBBNjlBRSIsDQogICAgICAgICAgICAgICAgICAgICAgIiNmZjAwMDAiLCAjcmVkDQogICAgICAgICAgICAgICAgICAgICAgIiNBNUJDNDUiLA0KICAgICAgICAgICAgICAgICAgICAgICIjZmYwMDAwIiwgI3JlZA0KICAgICAgICAgICAgICAgICAgICAgICIjMzI4MzQ5IiwNCiAgICAgICAgICAgICAgICAgICAgICAiI2ZmMDAwMCIsICNyZWQNCiAgICAgICAgICAgICAgICAgICAgICAiI0Y0Q0UyOSIsIA0KICAgICAgICAgICAgICAgICAgICAgICIjZmYwMDAwIiwgI3JlZA0KICAgICAgICAgICAgICAgICAgICAgICIjQTM2NDM1IiwNCiAgICAgICAgICAgICAgICAgICAgICAiI2ZmMDAwMCIsICNyZWQNCiAgICAgICAgICAgICAgICAgICAgICAiIzY5NDAyNyIpLA0KICAgICAgICAgICAgICAgICAgICAgICMiI2ZmMDAwMCIpLCAjcmVkIGZvciBzb21lIHJlYXNvbiBjb3VsZCBtYWtlIDcuNSwgaSB3b250IG5lZWQgaXQNCiAgICAgICAgICAgICAgICAgICAgICAgdmFsdWVzPXJlc2NhbGUoYygxLDEuNSwgMiwgMi41LCAzLCAzLjUsIDQsIDQuNSwgNSwgNS41LCA2LCA2LjUsIDcpKSwNCiAgICAgICAgICAgICAgICAgICAgICAgZ3VpZGU9ImNvbG9yYmFyIikgDQoNCiMgVGhlbWUsIG1hcmdpbnMgbWFudWFsbHkgZWRpdGVkDQpnMjwtIGcyICsgdGhlbWUoICAgIA0KICAgICAgICAgICAgICBheGlzLnRpY2tzPWVsZW1lbnRfYmxhbmsoKSwgDQogICAgICAgICAgICAgIGF4aXMudGV4dC54PWVsZW1lbnRfYmxhbmsoKSwgDQogICAgICAgICAgICAgIGF4aXMudGV4dC55PWVsZW1lbnRfYmxhbmsoKSwgDQogICAgICAgICAgICAgIGF4aXMudGl0bGUueD1lbGVtZW50X2JsYW5rKCksIA0KICAgICAgICAgICAgICBheGlzLnRpdGxlLnk9ZWxlbWVudF9ibGFuaygpLA0KICAgICAgICAgICAgICBwbG90Lm1hcmdpbiA9IHVuaXQoYygwLCAwLCAtMi41LCA3LjUpLCAiY20iKSwgI2hlcmUgYWRkIHRoZSBzcGFjZQ0KICAgICAgICAgICAgICBsZWdlbmQucG9zaXRpb24gPSAnbm9uZScpIA0KZzINCmBgYA0KDQogIEEgbG90IG9mIHdoaXRlIHNwYWNlLi4uDQoNCmBgYHtyICwgaW5jbHVkZT1UUlVFLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFLCBldmFsPVRSVUUsIGVjaG89VFJVRSwgZmlnLmhlaWdodCA9IDYsIGZpZy53aWR0aCA9IDZ9DQojIHJheQ0KcmF5c2hhZGVyOjpwbG90X2dnKGcyLCB3aWR0aCA9IDEyLCBoZWlnaHQgPSA2LCBzY2FsZSA9IDE1LCBtdWx0aWNvcmUgPSBUUlVFLCBvZmZzZXRfZWRnZXM9VFJVRSwgd2luZG93c2l6ZSA9IGMoODAwLCA4MDApKQ0KDQojZG8gb25lIGltYWdlLCB3aXRoIGEgc2V0IG9mIHBhcmFtZXRlcnMNCnJheXNoYWRlcjo6cmVuZGVyX2NhbWVyYShmb3Y9IDcwLCANCiAgICAgICAgICAgICAgICAgICAgICAgICB6b29tID0gMC4wNSwgDQogICAgICAgICAgICAgICAgICAgICAgICAgdGhldGEgPSAxMDAsIA0KICAgICAgICAgICAgICAgICAgICAgICAgIHBoaSA9IDE4KQ0KDQpyYXlzaGFkZXI6OnJlbmRlcl9kZXB0aChmb2N1cyA9IDAuNzIsIA0KICAgICAgICAgICAgICAgICAgICAgICBmb2NhbGxlbmd0aCA9IDI1MCkNCmBgYA0KDQpgYGB7ciAsIGluY2x1ZGU9VFJVRSwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRSwgZXZhbD1UUlVFLCBlY2hvPVRSVUUsIGZpZy5oZWlnaHQgPSA2LCBmaWcud2lkdGggPSA2fQ0KI2RvIG9uZSBpbWFnZSwgd2l0aCBhIHNldCBvZiBwYXJhbWV0ZXJzDQpyYXlzaGFkZXI6OnJlbmRlcl9jYW1lcmEoZm92PSA3MCwgDQogICAgICAgICAgICAgICAgICAgICAgICAgem9vbSA9IDAuMDYsIA0KICAgICAgICAgICAgICAgICAgICAgICAgIHRoZXRhID0gMjg1LCANCiAgICAgICAgICAgICAgICAgICAgICAgICBwaGkgPSAxMikNCg0KcmF5c2hhZGVyOjpyZW5kZXJfZGVwdGgoZm9jdXMgPSAwLjc0LCANCiAgICAgICAgICAgICAgICAgICAgICAgZm9jYWxsZW5ndGggPSAyNTAsDQogICAgICAgICAgICAgICAgICAgICAgIGNsZWFyID0gVFJVRSkNCmBgYA0KDQogIEkgY2hvb3NlIGRlIGZpcnN0IG9wdGlvbiwgbG9zaW5nIHNvbWUgb2YgdGhlIG1hcCBidXQgbWFpbnRhaW5lZCB0aGUgc2hhcnBuZXNzIG9mIHRoZSB0aWxlcy4NCg0KIyMgVGhlIGJpZyBmb3IgbG9vcA0KICANCiAgV2UgaGF2ZSA3NyBwb3NpdGlvbnMgb2YgdGhlIGJvYXQuIExldMK0cyBtYWtlIDc3IHBob3RvcyBvZiB0aGUgb2JqZWN0IHRoZW4uIFdlIGhhdmUgdG8gc3RhcnQgcGxheWluZyB3aXRoIHRoZSBwb2ludCBvZiB2aWV3IGFuZCBjb25zdHJ1Y3QgbGlzdHMgKHdpdGggNzcgZWxlbWVudHMpIGZvciBldmVyeSBwYXJhbWV0ZXIgdXNlZC4gDQogIA0KICBMZXTCtHMgcGxvdCBmaXZlIHBvaW50IG9mIHZpZXc6IA0KICANCiAgKiBzdGFydGluZyB0aGUgam9ybmV5IGF0IHRoZSBjb2FzdCBvZiBTcGFpbg0KICAqIGNvYXN0IG9mIEFmcmljYSANCiAgKiBhcnJpdmluZyBhdCBBbWVyaWNhIA0KICAqIGRlcGFydHVyZSBvZiBjZW50cmFsIEFtZXJpY2ENCiAgKiBiYWNrIGhvbWUNCiAgDQoNCmBgYHtyICwgaW5jbHVkZT1UUlVFLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFLCBldmFsPVRSVUUsIGVjaG89VFJVRSwgZmlnLmhlaWdodCA9IDQsIGZpZy53aWR0aCA9IDR9IA0KIyBkYXRhIHdpdGhvdXQgbWlzc2luZw0KZDE8LW5hLm9taXQoZCklPiUgDQogIGRwbHlyOjpmaWx0ZXIoUm93ICVpbiUgYyggcGFzdGUwKCJSIiwgMTE1OjE4MikgKSklPiUgIyBzZWxlY3Rpb24gb2Ygcm93cw0KICBkcGx5cjo6ZmlsdGVyKEhpZ2h0ICVpbiUgYyggcGFzdGUwKCJWIiwgMTMzOjIwMCkgKSkgIyBzZWxlY3Rpb24gb2YgY29sdW1ucw0KDQojIG9uZSBwb2ludCBvZiB0aGUgdHJpcA0KZDFbIGQxJFJvdz09cGFzdGUwKCJSIixsb25baV0pICYgZDEkSGlnaHQ9PXBhc3RlMCgiViIsbGF0W2ldKSAsIF0kVmFsdWU8LWQxWyBkMSRSb3c9PXBhc3RlMCgiUiIsbG9uW2ldKSAmIGQxJEhpZ2h0PT1wYXN0ZTAoIlYiLGxhdFtpXSkgLCBdJFZhbHVlICswLjUNCg0KIyBjcmVhdGUgZ3JhcGgNCmcyPC0gZ2dwbG90Mjo6Z2dwbG90KGQxLCBhZXMoeSA9IFJvdywgeCA9IEhpZ2h0LCBmaWxsID0gVmFsdWUpKSArDQogICAgICAgICAgICAgICAgICAgIGdlb21fdGlsZShjb2xvciA9ICJncmF5IikgICsNCiAgICAgICAgICAgICAgICAgICAgY29vcmRfZXF1YWwoKSArICMgc2V0IGVhY2ggdGlsZSB0byBiZSBzcXVhcmVkLCBub3QgcmVjdGFuZ3VsYXINCiAgICAgICAgICAgICAgICAgICAgbGFicyh4PU5VTEwsIHk9TlVMTCkrICMgdGFrZSBvdXQgdGhlIGF4aXMgbGVnZW5kcw0KICAgICAgICAgICAgICAgICAgICBzY2FsZV9maWxsX2dyYWRpZW50bihjb2xvdXJzPWMoDQogICAgICAgICAgICAgICAgICAgICAgIiM2Qzk4QzkiLCANCiAgICAgICAgICAgICAgICAgICAgICAiI2ZmMDAwMCIsICNyZWQNCiAgICAgICAgICAgICAgICAgICAgICAiIzBBNjlBRSIsDQogICAgICAgICAgICAgICAgICAgICAgIiNmZjAwMDAiLCAjcmVkDQogICAgICAgICAgICAgICAgICAgICAgIiNBNUJDNDUiLA0KICAgICAgICAgICAgICAgICAgICAgICIjZmYwMDAwIiwgI3JlZA0KICAgICAgICAgICAgICAgICAgICAgICIjMzI4MzQ5IiwNCiAgICAgICAgICAgICAgICAgICAgICAiI2ZmMDAwMCIsICNyZWQNCiAgICAgICAgICAgICAgICAgICAgICAiI0Y0Q0UyOSIsIA0KICAgICAgICAgICAgICAgICAgICAgICIjZmYwMDAwIiwgI3JlZA0KICAgICAgICAgICAgICAgICAgICAgICIjQTM2NDM1IiwNCiAgICAgICAgICAgICAgICAgICAgICAiI2ZmMDAwMCIsICNyZWQNCiAgICAgICAgICAgICAgICAgICAgICAiIzY5NDAyNyIpLA0KICAgICAgICAgICAgICAgICAgICAgICMiI2ZmMDAwMCIpLCAjcmVkIGZvciBzb21lIHJlYXNvbiBjb3VsbmQgbWFrZSA3LjUsIEkgd29udCBuZWVkIGl0DQogICAgICAgICAgICAgICAgICAgICAgIHZhbHVlcz1yZXNjYWxlKGMoMSwxLjUsIDIsIDIuNSwgMywgMy41LCA0LCA0LjUsIDUsIDUuNSwgNiwgNi41LCA3KSksDQogICAgICAgICAgICAgICAgICAgICAgIGd1aWRlPSJjb2xvcmJhciIpIA0KDQojIFRoZW1lLCBtYXJnaW5zIG1hbnVhbGx5IGVkaXRlZA0KZzI8LSBnMiArIHRoZW1lKCAgICANCiAgICAgICAgICAgICAgYXhpcy50aWNrcz1lbGVtZW50X2JsYW5rKCksIA0KICAgICAgICAgICAgICBheGlzLnRleHQueD1lbGVtZW50X2JsYW5rKCksIA0KICAgICAgICAgICAgICBheGlzLnRleHQueT1lbGVtZW50X2JsYW5rKCksIA0KICAgICAgICAgICAgICBheGlzLnRpdGxlLng9ZWxlbWVudF9ibGFuaygpLCANCiAgICAgICAgICAgICAgYXhpcy50aXRsZS55PWVsZW1lbnRfYmxhbmsoKSwNCiAgICAgICAgICAgICAgcGxvdC5tYXJnaW4gPSB1bml0KGMoMCwgMCwgMCwgMCksICJjbSIpLCANCiAgICAgICAgICAgICAgbGVnZW5kLnBvc2l0aW9uID0gJ25vbmUnKSANCg0KIyByYXkNCnJheXNoYWRlcjo6cGxvdF9nZyhnMiwgd2lkdGggPSA2LCBoZWlnaHQgPSA2LCBzY2FsZSA9IDMwLCBtdWx0aWNvcmUgPSBUUlVFLCBvZmZzZXRfZWRnZXM9VFJVRSwgd2luZG93c2l6ZSA9IGMoODAwLCA4MDApKQ0KDQojZG8gb25lIGltYWdlLCB3aXRoIGEgc2V0IG9mIHBhcmFtZXRlcnMNCnJheXNoYWRlcjo6cmVuZGVyX2NhbWVyYShmb3Y9IDcwLCANCiAgICAgICAgICAgICAgICAgICAgICAgICB6b29tID0gMC4xMiwgDQogICAgICAgICAgICAgICAgICAgICAgICAgdGhldGEgPSAxMDAsIA0KICAgICAgICAgICAgICAgICAgICAgICAgIHBoaSA9IDE4KQ0KDQpyYXlzaGFkZXI6OnJlbmRlcl9kZXB0aChmb2N1cyA9IDAuNjYsIA0KICAgICAgICAgICAgICAgICAgICAgICBmb2NhbGxlbmd0aCA9IDI1MCkNCmBgYA0KDQpgYGB7ciAsIGluY2x1ZGU9VFJVRSwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRSwgZXZhbD1UUlVFLCBlY2hvPVRSVUUsIGZpZy5oZWlnaHQgPSA0LCBmaWcud2lkdGggPSA0fSANCiNkbyBvbmUgaW1hZ2UsIHdpdGggYSBzZXQgb2YgcGFyYW1ldGVycw0KcmF5c2hhZGVyOjpyZW5kZXJfY2FtZXJhKGZvdj0gNzAsIA0KICAgICAgICAgICAgICAgICAgICAgICAgIHpvb20gPSAwLjE1LCANCiAgICAgICAgICAgICAgICAgICAgICAgICB0aGV0YSA9IDk1LCANCiAgICAgICAgICAgICAgICAgICAgICAgICBwaGkgPSAyNSkNCg0KcmF5c2hhZGVyOjpyZW5kZXJfZGVwdGgoZm9jdXMgPSAwLjcxLCANCiAgICAgICAgICAgICAgICAgICAgICAgZm9jYWxsZW5ndGggPSAyNTApDQoNCmBgYA0KDQogIEFycml2aW5nIGF0IGxhbmQuLi4gDQoNCmBgYHtyICwgaW5jbHVkZT1UUlVFLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFLCBldmFsPVRSVUUsIGVjaG89VFJVRSwgZmlnLmhlaWdodCA9IDQsIGZpZy53aWR0aCA9IDR9IA0KI2RvIG9uZSBpbWFnZSwgd2l0aCBhIHNldCBvZiBwYXJhbWV0ZXJzDQpyYXlzaGFkZXI6OnJlbmRlcl9jYW1lcmEoZm92PSA3MCwgDQogICAgICAgICAgICAgICAgICAgICAgICAgem9vbSA9IDAuMDksIA0KICAgICAgICAgICAgICAgICAgICAgICAgIHRoZXRhID05MCwgDQogICAgICAgICAgICAgICAgICAgICAgICAgcGhpID0gMTQpDQoNCnJheXNoYWRlcjo6cmVuZGVyX2RlcHRoKGZvY3VzID0gMC44OCwgDQogICAgICAgICAgICAgICAgICAgICAgIGZvY2FsbGVuZ3RoID0gMjUwKQ0KDQpgYGANCg0KICBUaGUgcmV0dXJuLi4uDQoNCmBgYHtyICwgaW5jbHVkZT1UUlVFLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFLCBldmFsPVRSVUUsIGVjaG89VFJVRSwgZmlnLmhlaWdodCA9IDQsIGZpZy53aWR0aCA9IDR9IA0KI2RvIG9uZSBpbWFnZSwgd2l0aCBhIHNldCBvZiBwYXJhbWV0ZXJzDQpyYXlzaGFkZXI6OnJlbmRlcl9jYW1lcmEoZm92PSA3MCwgDQogICAgICAgICAgICAgICAgICAgICAgICAgem9vbSA9IDAuMzQsIA0KICAgICAgICAgICAgICAgICAgICAgICAgIHRoZXRhID0yODUsIA0KICAgICAgICAgICAgICAgICAgICAgICAgIHBoaSA9IDMzKQ0KDQpyYXlzaGFkZXI6OnJlbmRlcl9kZXB0aChmb2N1cyA9IDAuNzAsIA0KICAgICAgICAgICAgICAgICAgICAgICBmb2NhbGxlbmd0aCA9IDI1MCkNCg0KYGBgDQogDQogRW5kIHBvaW50IG9mIHZpZXcNCg0KYGBge3IgLCBpbmNsdWRlPVRSVUUsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0UsIGV2YWw9VFJVRSwgZWNobz1UUlVFLCBmaWcuaGVpZ2h0ID0gNCwgZmlnLndpZHRoID0gNH0gDQojZG8gb25lIGltYWdlLCB3aXRoIGEgc2V0IG9mIHBhcmFtZXRlcnMNCnJheXNoYWRlcjo6cmVuZGVyX2NhbWVyYShmb3Y9IDcwLCANCiAgICAgICAgICAgICAgICAgICAgICAgICB6b29tID0gMC4zNywgDQogICAgICAgICAgICAgICAgICAgICAgICAgdGhldGEgPTM0MCwgDQogICAgICAgICAgICAgICAgICAgICAgICAgcGhpID0gNTcpDQoNCnJheXNoYWRlcjo6cmVuZGVyX2RlcHRoKGZvY3VzID0gMC44MiwgDQogICAgICAgICAgICAgICAgICAgICAgIGZvY2FsbGVuZ3RoID0gMjUwKQ0KDQpgYGANCiANCiBJbnRlcnBvbGF0ZSBhbGwgdGhlIHZpZXdzLg0KDQpgYGB7ciAsIGluY2x1ZGU9VFJVRSwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRSwgZXZhbD1GQUxTRSwgZWNobz1UUlVFLCBmaWcuaGVpZ2h0ID0gNiwgZmlnLndpZHRoID0gNn0gDQojc2VxdWVuY2VzIG9mIHBhcmFtZXRlcnMgKHdlIGFscmVhZHkgaGF2ZSBsYXQgbG9uIGZvciBjb2x1bWJ1cyBwb3NpdGlvbiBjb21pbmcgZnJvbSB0aGUgcHJldmlvcyBjaHVuaykNCkxpc3RfZm92IDwtICAgICAgICAgYyhyZXAoNzAsIDc3KSkNCkxpc3Rfem9vbSA8LSAgICAgICAgYyhzZXEoMC4xMiwgMC4xMiwgbGVuZ3RoLm91dCA9IDEwKSwgIzEwDQogICAgICAgICAgICAgICAgICAgICAgc2VxKDAuMTIsIDAuMDksIGxlbmd0aC5vdXQgPSAzMCksICMyMC00MA0KICAgICAgICAgICAgICAgICAgICAgIHNlcSgwLjM0LCAwLjM3LCBsZW5ndGgub3V0ID0gMzcpKSAjNDAtNzcNCkxpc3RfdGhldGEgPC0gICAgICAgYyhzZXEoMTAwLCA5NSwgbGVuZ3RoLm91dCA9IDEwKSwgIzEwDQogICAgICAgICAgICAgICAgICAgICAgc2VxKDk1LCA5MCwgbGVuZ3RoLm91dCA9IDMwKSwgIzQwLTc3DQogICAgICAgICAgICAgICAgICAgICAgc2VxKDI4NSwgMzQwLCBsZW5ndGgub3V0ID0gMzcpKSAjNDAtNzcNCkxpc3RfcGhpIDwtICAgICAgICAgYyhzZXEoMTgsIDI1LCBsZW5ndGgub3V0ID0gMTApLCAjMTANCiAgICAgICAgICAgICAgICAgICAgICBzZXEoMjUsIDE0LCBsZW5ndGgub3V0ID0gMzApLCAjMjAtNDANCiAgICAgICAgICAgICAgICAgICAgICBzZXEoMzMsIDU3LCBsZW5ndGgub3V0ID0gMzcpKSAjNDAtNzcNCkxpc3RfZm9jdXMgPC0gICAgICAgYyhzZXEoMC42NiwgMC43MSwgbGVuZ3RoLm91dCA9IDEwKSwgIzEwDQogICAgICAgICAgICAgICAgICAgICAgc2VxKDAuNzEsIDAuODgsIGxlbmd0aC5vdXQgPSAzMCksICMyMC00MA0KICAgICAgICAgICAgICAgICAgICAgIHNlcSgwLjcxLCAwLjc0LCBsZW5ndGgub3V0ID0gMTApLCAjNDAtNTANCiAgICAgICAgICAgICAgICAgICAgICBzZXEoMC43NCwgMC44MSwgbGVuZ3RoLm91dCA9IDEwKSwgIzUwLTYwDQogICAgICAgICAgICAgICAgICAgICAgc2VxKDAuODEsIDAuODIsIGxlbmd0aC5vdXQgPSAxNykpICM2MC03Nw0KTGlzdF9mb2NhbGxlbmd0aCA8LSBjKHJlcCgyNTAsIDc3KSkNCg0KIyBkYXRhIHdpdGhvdXQgbWlzc2luZywgc2VsZWN0ZWQNCmQxPC1uYS5vbWl0KGQpJT4lIA0KICBkcGx5cjo6ZmlsdGVyKFJvdyAlaW4lIGMoIHBhc3RlMCgiUiIsIDExNToxODIpICkpJT4lICMgc2VsZWN0aW9uIG9mIHJvd3MNCiAgZHBseXI6OmZpbHRlcihIaWdodCAlaW4lIGMoIHBhc3RlMCgiViIsIDEzMzoyMDApICkpICMgc2VsZWN0aW9uIG9mIGNvbHVtbnMNCg0KIyBzdGFydCB0aGUgbG9vcA0KZm9yIChpIGluIDE6NzcpIHsgDQoNCiMgb25lIHBvaW50IG9mIHRoZSB0cmlwDQpkMVsgZDEkUm93PT1wYXN0ZTAoIlIiLGxvbltpXSkgJiBkMSRIaWdodD09cGFzdGUwKCJWIixsYXRbaV0pICwgXSRWYWx1ZTwtZDFbIGQxJFJvdz09cGFzdGUwKCJSIixsb25baV0pICYgZDEkSGlnaHQ9PXBhc3RlMCgiViIsbGF0W2ldKSAsIF0kVmFsdWUgKzAuNQ0KDQojIGNyZWF0ZSBncmFwaA0KZzI8LSBnZ3Bsb3QyOjpnZ3Bsb3QoZDEsIGFlcyh5ID0gUm93LCB4ID0gSGlnaHQsIGZpbGwgPSBWYWx1ZSkpICsNCiAgICAgICAgICAgICAgICAgICAgZ2VvbV90aWxlKGNvbG9yID0gImdyYXkiKSAgKw0KICAgICAgICAgICAgICAgICAgICBjb29yZF9lcXVhbCgpICsgIyBzZXQgZWFjaCB0aWxlIHRvIGJlIHNxdWFyZWQsIG5vdCByZWN0YW5ndWxhcg0KICAgICAgICAgICAgICAgICAgICBsYWJzKHg9TlVMTCwgeT1OVUxMKSsgIyB0YWtlIG91dCB0aGUgYXhpcyBsZWdlbmRzDQogICAgICAgICAgICAgICAgICAgIHNjYWxlX2ZpbGxfZ3JhZGllbnRuKGNvbG91cnM9YygNCiAgICAgICAgICAgICAgICAgICAgICAiIzZDOThDOSIsIA0KICAgICAgICAgICAgICAgICAgICAgICIjZmYwMDAwIiwgI3JlZA0KICAgICAgICAgICAgICAgICAgICAgICIjMEE2OUFFIiwNCiAgICAgICAgICAgICAgICAgICAgICAiI2ZmMDAwMCIsICNyZWQNCiAgICAgICAgICAgICAgICAgICAgICAiI0E1QkM0NSIsDQogICAgICAgICAgICAgICAgICAgICAgIiNmZjAwMDAiLCAjcmVkDQogICAgICAgICAgICAgICAgICAgICAgIiMzMjgzNDkiLA0KICAgICAgICAgICAgICAgICAgICAgICIjZmYwMDAwIiwgI3JlZA0KICAgICAgICAgICAgICAgICAgICAgICIjRjRDRTI5IiwgDQogICAgICAgICAgICAgICAgICAgICAgIiNmZjAwMDAiLCAjcmVkDQogICAgICAgICAgICAgICAgICAgICAgIiNBMzY0MzUiLA0KICAgICAgICAgICAgICAgICAgICAgICIjZmYwMDAwIiwgI3JlZA0KICAgICAgICAgICAgICAgICAgICAgICIjNjk0MDI3IiksDQogICAgICAgICAgICAgICAgICAgICAgIyIjZmYwMDAwIiksICNyZWQgZm9yIHNvbWUgcmVhc29uIGNvdWxuZCBtYWtlIDcuNSwgSSB3b250IG5lZWQgaXQNCiAgICAgICAgICAgICAgICAgICAgICAgdmFsdWVzPXJlc2NhbGUoYygxLDEuNSwgMiwgMi41LCAzLCAzLjUsIDQsIDQuNSwgNSwgNS41LCA2LCA2LjUsIDcpKSwNCiAgICAgICAgICAgICAgICAgICAgICAgZ3VpZGU9ImNvbG9yYmFyIikgDQoNCiMgVGhlbWUsIG1hcmdpbnMgbWFudWFsbHkgZWRpdGVkDQpnMjwtIGcyICsgdGhlbWUoICAgIA0KICAgICAgICAgICAgICBheGlzLnRpY2tzPWVsZW1lbnRfYmxhbmsoKSwgDQogICAgICAgICAgICAgIGF4aXMudGV4dC54PWVsZW1lbnRfYmxhbmsoKSwgDQogICAgICAgICAgICAgIGF4aXMudGV4dC55PWVsZW1lbnRfYmxhbmsoKSwgDQogICAgICAgICAgICAgIGF4aXMudGl0bGUueD1lbGVtZW50X2JsYW5rKCksIA0KICAgICAgICAgICAgICBheGlzLnRpdGxlLnk9ZWxlbWVudF9ibGFuaygpLA0KICAgICAgICAgICAgICBwbG90Lm1hcmdpbiA9IHVuaXQoYygwLCAwLCAwLCAwKSwgImNtIiksIA0KICAgICAgICAgICAgICBsZWdlbmQucG9zaXRpb24gPSAnbm9uZScpIA0KDQojIHJheQ0KcmF5c2hhZGVyOjpwbG90X2dnKGcyLCB3aWR0aCA9IDYsIGhlaWdodCA9IDYsIHNjYWxlID0gMzAsIG11bHRpY29yZSA9IFRSVUUsIG9mZnNldF9lZGdlcz1UUlVFLCB3aW5kb3dzaXplID0gYyg4MDAsIDgwMCkpDQoNCiMgbWFrZSBhIHBvaW50IG9mIHZpZXcNCnJheXNoYWRlcjo6cmVuZGVyX2NhbWVyYShmb3Y9IExpc3RfZm92W2ldLCANCiAgICAgICAgICAgICAgICAgICAgICAgICB6b29tID0gTGlzdF96b29tW2ldLCANCiAgICAgICAgICAgICAgICAgICAgICAgICB0aGV0YSA9IExpc3RfdGhldGFbaV0sIA0KICAgICAgICAgICAgICAgICAgICAgICAgIHBoaSA9IExpc3RfcGhpW2ldKQ0KDQojc2F2ZSBhIHBpY3R1cmUNCnJheXNoYWRlcjo6cmVuZGVyX2RlcHRoKGZpbGVuYW1lPXBhc3RlMCgiUGljIixpKSwNCiAgICAgICAgICAgICAgICAgICAgICAgIGZvY3VzID0gTGlzdF9mb2N1c1tpXSwgDQogICAgICAgICAgICAgICAgICAgICAgICBmb2NhbGxlbmd0aCA9IExpc3RfZm9jYWxsZW5ndGhbaV0sDQogICAgICAgICAgICAgICAgICAgICAgICBjbGVhciA9IFRSVUUpDQoNCiMgb25lIHBvaW50IG91dCBvZiB0aGUgdHJpcA0KZDFbIGQxJFJvdz09cGFzdGUwKCJSIixsb25baV0pICYgZDEkSGlnaHQ9PXBhc3RlMCgiViIsbGF0W2ldKSAsIF0kVmFsdWU8LWQxWyBkMSRSb3c9PXBhc3RlMCgiUiIsbG9uW2ldKSAmIGQxJEhpZ2h0PT1wYXN0ZTAoIlYiLGxhdFtpXSkgLCBdJFZhbHVlIC0gKDAuNSkNCn0NCg0KYGBgDQoNCiAgQWxsIHRvZ2V0aGVyIG5vdyENCg0KYGBge3IgLCBpbmNsdWRlPVRSVUUsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0UsIGV2YWw9RkFMU0UsIGVjaG89VFJVRX0NCiNsaXN0IG9mIGltYWdlcywgcmV2ZXJzZWQgYW5kIGNvbmNhdGVuYXRlZA0KZnJhbWVzIDwtIHBhc3RlMCgiUGljIiwgMTo3NywgIi5wbmciKQ0KI2ZyYW1lcyA8LWMoZnJhbWVzLCByZXYoZnJhbWVzKSkNCg0KI2RvIHRoZSBtYWdpY2shDQptIDwtIG1hZ2ljazo6aW1hZ2VfcmVhZChmcmFtZXMpDQptIDwtIG1hZ2ljazo6aW1hZ2VfYW5pbWF0ZShtLCBmcHMgPSA1KQ0KDQojc2F2ZSB0aGUgZ2lmDQptYWdpY2s6OmltYWdlX3dyaXRlKG0sICJDb2x1bWJ1czQuZ2lmIikNCmBgYA0KDQpEdWUgdG8gdGhlIGxpbWl0cyBvbiBnaXRwYWdlcyBoZXJlIHRoZSANCg0KPHAxPioqPGEgaHJlZj0iaHR0cHM6Ly9naXRodWIuY29tL2FydGh1cndlbGxlL1JheXNoYWRlcldhbGt0aHJvdWdoL2Jsb2IvbWFzdGVyL0NvbHVtYnVzMy5naWYiPkxJTksgVE8gR0lGPC9hPioqLjwvcDE+DQogIA0KICBZZXAsIHRoYXQgaXQhDQogIA0KIyMjIEEgbGl0dGxlIG1hcCBvbiB0aGUgdG9wIG9mIHRoZSBnaWYuDQoNCiAgTGV0wrRzIGltcHJvdmUgdGhlIGdpZiB3aXRoIGEgd2VsbCBiZWhhdmUgbWFwLCBhbmQgc29tZSBpbmZvcm1hdGlvbiBvbiB0b3AuIEZpcnN0IGEgbGl0dGxlIG1hcCB0byBiZSBwdXQgb24gdG9wIG9mIHRoZSB2aWRlby9naWYuIEl0wrRzIGp1c3QgYSBjb3B5IG9mIHByZXZpb3VzIHdvcmssIHdpdGggc21hbGwgY2hhbmdlcy4NCg0KYGBge3IgLCBpbmNsdWRlPVRSVUUsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0UsIGV2YWw9RkFMU0UsIGVjaG89VFJVRSwgZmlnLmhlaWdodCA9IDIsIGZpZy53aWR0aCA9IDJ9IA0KI2NyZWF0ZSBhIGNvcHkgb2YgdGhlIGRhdGEsIHdpdGggbm8gbWlzc2luZywgYW5kIGp1c3QgYSBwYXJ0DQpkMTwtbmEub21pdChkKSAgJT4lIA0KICBkcGx5cjo6ZmlsdGVyKFJvdyAlaW4lIGMoIHBhc3RlMCgiUiIsIDEyNToxNzUpICkpJT4lDQogIGRwbHlyOjpmaWx0ZXIoSGlnaHQgJWluJSBjKCBwYXN0ZTAoIlYiLCAxNDQ6MTk1KSApKQ0KDQoNCiNjcmVhdGUgdHdvIHZlcmN0b3Mgd2l0aCB0aGUgcG9zaXRpb25zIG9mIHRoZSB0cmF2ZWwNCmxvbjwtYyggIDE1NSwxNTQsMTUzLDE1MiwxNTEsMTUwLDE0OSwxNDgsDQogICAgICAgICByZXAoMTQ4LDMxKSwNCiAgICAgICAgIDE0NywxNDcsMTQ3LDE0NywxNDcsMTQ2LDE0NiwxNDYsMTQ2LA0KICAgICAgICAgMTQ3LDE0OCwxNDksMTUwLDE1MSwxNTIsMTUzLDE1NCwxNTUsMTU2LDE1NywNCiAgICAgICAgIHJlcCgxNTcsMTgpKQ0KDQpsYXQ8LWMoICAxODUsMTg0LDE4MywxODIsMTgxLDE4MCwxNzksMTc5LA0KICAgICAgICAgMTc4OjE0OCwNCiAgICAgICAgIDE0NywxNDgsMTQ5LDE1MCwxNTEsMTUxLDE1MiwxNTMsMTU0LA0KICAgICAgICAgMTU1LDE1NiwxNTcsMTU4LDE1OSwxNjAsMTYxLDE2MiwxNjMsMTY0LDE2NSwNCiAgICAgICAgIDE2NjoxODMpDQoNCg0KZm9yIChpIGluIDE6NzcpIHsgDQojIG9uZSBwb2ludCBvZiB0aGUgdHJpcA0KZDFbIGQxJFJvdz09cGFzdGUwKCJSIixsb25baV0pICYgZDEkSGlnaHQ9PXBhc3RlMCgiViIsbGF0W2ldKSAsIF0kVmFsdWU8LWQxWyBkMSRSb3c9PXBhc3RlMCgiUiIsbG9uW2ldKSAmIGQxJEhpZ2h0PT1wYXN0ZTAoIlYiLGxhdFtpXSkgLCBdJFZhbHVlICswLjUNCg0KI2NyZWF0ZSBncmFwaA0KZzI8LSBnZ3Bsb3QyOjpnZ3Bsb3QoZDEsIGFlcyh5ID0gUm93LCB4ID0gSGlnaHQsIGZpbGwgPSBWYWx1ZSkpICsNCiAgICAgICAgICAgICAgICAgICAgZ2VvbV90aWxlKCkgICsNCiAgICAgICAgICAgICAgICAgICAgY29vcmRfZXF1YWwoKSArICMgc2V0IGVhY2ggdGlsZSB0byBiZSBzcXVhcmVkLCBub3QgcmVjdGFuZ3VsYXINCiAgICAgICAgICAgICAgICAgICAgbGFicyh4PU5VTEwsIHk9TlVMTCkrICMgdGFrZSBvdXQgdGhlIGF4aXMgbGVnZW5kcw0KICAgICAgICAgICAgICAgICAgICBzY2FsZV9maWxsX2dyYWRpZW50bihjb2xvdXJzPWMoDQogICAgICAgICAgICAgICAgICAgICAgIiM2Qzk4QzkiLCANCiAgICAgICAgICAgICAgICAgICAgICAiYmxhY2siLCAjcmVkDQogICAgICAgICAgICAgICAgICAgICAgIiMwQTY5QUUiLA0KICAgICAgICAgICAgICAgICAgICAgICJibGFjayIsICNyZWQNCiAgICAgICAgICAgICAgICAgICAgICAiI0E1QkM0NSIsDQogICAgICAgICAgICAgICAgICAgICAgImJsYWNrIiwgI3JlZA0KICAgICAgICAgICAgICAgICAgICAgICIjMzI4MzQ5IiwNCiAgICAgICAgICAgICAgICAgICAgICAiYmxhY2siLCAjcmVkDQogICAgICAgICAgICAgICAgICAgICAgIiNGNENFMjkiLCANCiAgICAgICAgICAgICAgICAgICAgICAiYmxhY2siLCAjcmVkDQogICAgICAgICAgICAgICAgICAgICAgIiNBMzY0MzUiLA0KICAgICAgICAgICAgICAgICAgICAgICJibGFjayIsICNyZWQNCiAgICAgICAgICAgICAgICAgICAgICAiIzY5NDAyNyIpLA0KICAgICAgICAgICAgICAgICAgICAgICMiI2ZmMDAwMCIpLCAjcmVkIGZvciBzb21lIHJlYXNvbiBjb3VsbmQgbWFrZSA3LjUsIEkgd29udCBuZWVkIGl0DQogICAgICAgICAgICAgICAgICAgICAgIHZhbHVlcz1yZXNjYWxlKGMoMSwxLjUsIDIsIDIuNSwgMywgMy41LCA0LCA0LjUsIDUsIDUuNSwgNiwgNi41LCA3KSksDQogICAgICAgICAgICAgICAgICAgICAgIGd1aWRlPSJjb2xvcmJhciIpIA0KDQojIFRoZW1lLCBtYXJnaW5zIG1hbnVhbGx5IGVkaXRlZA0KZzI8LSBnMiArIHRoZW1lKCAgICANCiAgICAgICAgICAgICAgYXhpcy50aWNrcz1lbGVtZW50X2JsYW5rKCksIA0KICAgICAgICAgICAgICBheGlzLnRleHQueD1lbGVtZW50X2JsYW5rKCksIA0KICAgICAgICAgICAgICBheGlzLnRleHQueT1lbGVtZW50X2JsYW5rKCksIA0KICAgICAgICAgICAgICBheGlzLnRpdGxlLng9ZWxlbWVudF9ibGFuaygpLCANCiAgICAgICAgICAgICAgYXhpcy50aXRsZS55PWVsZW1lbnRfYmxhbmsoKSwNCiAgICAgICAgICAgICAgcGxvdC5tYXJnaW4gPSB1bml0KGMoMCwgMCwgMCwgMCksICJjbSIpLCANCiAgICAgICAgICAgICAgbGVnZW5kLnBvc2l0aW9uID0gJ25vbmUnKSANCg0KZ2dwbG90Mjo6Z2dzYXZlKGZpbGVuYW1lID0gcGFzdGUwKCJGIixpLCIucG5nIiksIHBsb3QgPSBnMiwgd2lkdGggPSAyLCBoZWlnaHQgPSAyLCB1bml0cyA9ICJjbSIgKQ0KDQp9DQpnMg0KYGBgDQoNCiMjIyBBbGwgdGhlIGltYWdlcyB0b2dldGhlciBvbiBhIGdpZg0KDQogIExldMK0cyBtYWtlIHNvbWUgdGV4dCB0byBiZSBwdXQgdG9nZXRoZXIgb24gdGhlIGdpZiwgdGhlIGRhdGUgb2YgdGhlIHZveWFnZS4gQWRkaXRpb25hbGx5IGxldMK0cyBwdXQgbXkgdHdpdHRlciBoYW5kbGUgb24gdGhlIGJvdHRvbSBvZiB0aGUgZ2lmLiANCiAgDQpgYGB7ciAsIGluY2x1ZGU9VFJVRSwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRSwgZXZhbD1GQUxTRSwgZWNobz1UUlVFfSANCg0KVGV4dDwtYygiMDMgYWdvIDE0OTIiLCByZXAoImFnbyAxNDkyIiwxMCksIHJlcCgic2VwIDE0OTIiLDIyKSwgcmVwKCJvdXQgMTQ5MiIsNiksIjEyIG91dCAxNDkyIiwgcmVwKCJvdXQgMTQ5MiIsNCksIHJlcCgibm92IDE0OTIiLDIpLCByZXAoImRlYyAxNDkyIiwxKSwgcmVwKCJqYW4gMTQ5MyIsNyksIHJlcCgiZmViIDE0OTMiLDIwKSwgcmVwKCJtYXIgMTQ5MyIsMiksICIwNCBtYXIgMTQ5MyIpDQoNCmZvciAoaSBpbiAzOTo0MSkgeyANCiAgDQojIGFsbCBtYWdpY2sgbWFuaXB1bGF0aW9uIG9mIGltYWdlcw0KbGl0dGxlX3BpYzwtbWFnaWNrOjppbWFnZV9yZWFkKHBhdGggPSBwYXN0ZTAoIkYiLGksIi5wbmciKSkNCg0KI3Rha2Ugb3V0IHNvbWUgdW5zdGFuZGFyZCBib3JkZXINCmxpdHRsZV9waWM8LW1hZ2ljazo6aW1hZ2VfdHJpbShsaXR0bGVfcGljKQ0KbGl0dGxlX3BpYzwtbWFnaWNrOjppbWFnZV9jcm9wKGxpdHRsZV9waWMsICIyMjRYMjI0IikNCg0KI2FkZCBibGFjayBib3JkZXIsIDIgcGl4ZWxzIA0KbGl0dGxlX3BpYzwtbWFnaWNrOjppbWFnZV9ib3JkZXIobGl0dGxlX3BpYywgImJsYWNrIiwgIjJYMiIpDQoNCiNyZWFkIHRoZSByYXlzaGFkZXIgaW1hZ2UNCmxhcmdlX3BpYzwtbWFnaWNrOjppbWFnZV9yZWFkKHBhdGggPSBwYXN0ZTAoIlBpYyIsaSwiLnBuZyIpKQ0KDQojbWFrZSBhIGNvbGFnZSBmcm9tIHRoZSB0d28gaW1hZ2VzDQpjb2xhZ2U8LW1hZ2ljazo6aW1hZ2VfZmxhdHRlbihjKGxhcmdlX3BpYywgbGl0dGxlX3BpYykpDQoNCiMgYWRkIHNvbWUgdGV4dA0KY29sYWdlIDwtIG1hZ2ljazo6aW1hZ2VfYW5ub3RhdGUoY29sYWdlLCB0ZXh0ID0gVGV4dFtpXSwgZm9udCA9ICJtb25vIiwgbG9jYXRpb24gPSAiKzgrMTgyIiwgYm94Y29sb3IgPSAid2hpdGUiLCBzaXplID0gMzApICAgDQpjb2xhZ2UgPC0gbWFnaWNrOjppbWFnZV9hbm5vdGF0ZShjb2xhZ2UsIHRleHQgPSAiQ29sdW1idXMgVm95YWdlIiwgZm9udCA9ICJ0aW1lcyIsIGxvY2F0aW9uID0gIis0KzQiLCBzaXplID0gMzAsIGJveGNvbG9yID0gIndoaXRlIikgIA0KY29sYWdlIDwtIG1hZ2ljazo6aW1hZ2VfYW5ub3RhdGUoY29sYWdlLCB0ZXh0ID0gIkBBcnRodXJXZWxsZSIsIGZvbnQgPSAibW9ubyIsIGxvY2F0aW9uID0gIis0KzcwOCIsIGJveGNvbG9yID0gIndoaXRlIiwgc2l6ZSA9IDI0KSAgDQoNCiNzYXZlIHdpdGggYW5vdGhlciBuYW1lDQptYWdpY2s6OmltYWdlX3dyaXRlKGltYWdlID0gY29sYWdlLCBwYXRoID0gcGFzdGUwKCJUIixpLCIucG5nIikpDQoNCn0NCg0KIyBBZGQgc29tZSB0ZXh0IGluIHNwZWNpZmljIGltYWdlcw0KVDE8LW1hZ2ljazo6aW1hZ2VfcmVhZChwYXRoID0gIlQ0MC5wbmciKQ0KVDEgPC0gbWFnaWNrOjppbWFnZV9hbm5vdGF0ZShUMSwgdGV4dCA9ICJBcnJpdmUgYXQgR3VhbmFoYW5pIElzbGFuZCIsIGZvbnQgPSAidGltZXMiLCBsb2NhdGlvbiA9ICIrMjg4KzgiLCBzaXplID0gNDAsIGJveGNvbG9yID0gIndoaXRlIikgDQptYWdpY2s6OmltYWdlX3dyaXRlKGltYWdlID0gVDEsIHBhdGggPSAiVDQwLnBuZyIpDQoNCmBgYA0KDQogIERvbmUhIEFuZCBpdCdzIHRpbWUgdG8gbWFrZSB0aGUgZmluYWwgZ2lmLg0KDQpgYGB7ciAsIGluY2x1ZGU9VFJVRSwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRSwgZXZhbD1GQUxTRSwgZWNobz1UUlVFfQ0KI2xpc3Qgb2YgaW1hZ2VzLCByZXZlcnNlZCBhbmQgY29uY2F0ZW5hdGVkDQpmcmFtZXMgPC0gcGFzdGUwKCJUIiwgMTo3NywgIi5wbmciKQ0KI2ZyYW1lcyA8LWMoZnJhbWVzLCByZXYoZnJhbWVzKSkNCg0KI2RvIHRoZSBtYWdpY2shDQptIDwtIG1hZ2ljazo6aW1hZ2VfcmVhZChmcmFtZXMpDQptIDwtIG1hZ2ljazo6aW1hZ2VfYW5pbWF0ZShtLCBmcHMgPSA1KQ0KDQojc2F2ZSB0aGUgZ2lmDQptYWdpY2s6OmltYWdlX3dyaXRlKG0sICJDb2x1bWJ1czEwLmdpZiIpDQpgYGANCg0KRHVlIHRvIHRoZSBsaW1pdHMgb24gZ2l0cGFnZXMgaGVyZSB0aGUgDQoNCjxwMT4qKjxhIGhyZWY9Imh0dHBzOi8vZ2l0aHViLmNvbS9hcnRodXJ3ZWxsZS9SYXlzaGFkZXJXYWxrdGhyb3VnaC9ibG9iLzJlN2EyNzRkNjExZmIwMjM1N2E5N2U1OTIxZWM0NGMwNDk2ODE5NDYvQ29sdW1idXMxMS5naWYiPkxJTksgVE8gRklOQUwgR0lGPC9hPioqLjwvcDE+DQoNCiAgRmluaXNoISBUaGF0wrRzIG15IHRha2Ugb24gdGhpcyBpc3N1ZS4gVHVybnMgb3V0IHRvIGJlIGEgbG9uZ2VyIHRha2UgdGhhbiBJIGV4cGVjdGVkLiANCiAgDQogIEkgaG9wZSB5b3UgZW5qb3kgdGhlIHJpZGUvc2FpbCBhcyB3ZWxsLiBUaGVyZSBpcyBvZiBjb3Vyc2UgdGhpbmdzIHRoYXQgY2FuIGJlIGRvbmUgYmV0dGVyLiBPbmUgdGhhdCBib3RoZXJlZCBtZSBhIGJpdCB3YXMgdGhlIGxhY2sgb2YgZnVuY3Rpb24gdXNpbmcvbWFraW5nLiBSZXBlYXRpbmcgdGhlIGNvZGUgd2hlbiBpdMK0cyBub3QgbmVjZXNzYXJ5IGlzIGEgYmFkIHByYWN0aWNlLCBidXQgSSBrZWVwIGl0IHRoYXQgd2F5IHNvIGl0IHdvdWxkIGJlIGNsZWFyZXIgZm9yIGFueSB1c2VyIHdoYXQncyBnb2luZyBvbiBpbiBldmVyeSBwYXJ0IG9mIHRoZSBjb2RlIChJIGNvdWxkIHBpbiBwb2ludCBzb21lIG9mIGNvZGUgbGluZXMgdGhhdCBsYWNrIGNsYXJpdHkhIFNvcnJ5ISkuIEZ1cnRoZXJtb3JlIEknbSBzdGlsbCBsZWFybmluZyBSIGNvZGUgZXZlcnkgZGF5LiBEb24ndCB0YWtlIG15IGNvZGUgYXQgZmFjZSB2YWx1ZS4gDQogIA0KICBJIGxlYXJuZWQgYSBsb3QgZG9pbmcgdGhpcyBsaXR0bGUgc2lkZSBwcm9qZWN0LiBHcmVhdGVzdCBzaG91dCBvdXQgdG8gdGhlIGd1eXMgdGhhdCBkaWQgdGhlIHJlYWwgYmFzZSB3b3JrLCAqKlR5bGVyIE1vcmdhbi1XYWxsKiogYW5kICoqRGlyayoqLg0KICANCiAgSSBzdXBwb3NlIEkgbXVzdCBjaXRlIGhlcmUgaW4gdGhlIGVuZCAqKjxhIGhyZWY9Imh0dHBzOi8vZ2l0aHViLmNvbS9yeWFudGltcGUvYnJpY2tyIj5icmlja3I8L2E+KiosIHdoaWNoIGlzIGEgcGFja2FnZSBmb3IgYnVpbGRpbmcgTEVHTyBicmlja3MgaW4gUi4gSSBoYWQgbmV2ZXIgdHJpZWQgdGhlIHBhY2thZ2UsIGJ1dCBJIGFtIHN1cmUsIGl0wrRzIHF1aXRlIGZ1biBhcyB3ZWxsLiBHaXZlIGl0IGEgZ28hDQogIA0KICANCiAgDQogIA0KDQo=