This notebook provides supplementary material for my thesis project titled: “Tackling Uncertainty in the Spatial Density Estimation from Mobile Network Operator data”. It focuses on the first module of the MNO-simulator workflow, the generation of network scenarios. Its goal is to provide the complete code for reproduction and in-depth analysis of the four different network scenarios. Further information of this project can be found here.

As mentioned on page 4 of the thesis article, we need to create three data sources:

  • "Operating area data: We assume the operating area to be discretized into a regular grid, e.g., 100m x 100m. Each square unit is termed as a tile, indexed in \(j = 1,2, \ldots, J\). \(u_j\) denotes the unknown non-negative number of mobile phones of the \(j\)th tile, stored in the column vector \(u := [u_{1}… u_{J}]^{T}\). This standardized unit helps us set a formalism framework that has been used in multiple studies beforehand [19, 22, 28].

  • Aggregated network event data: Let the \(i\)th element \(c_{i}\) of the column vector \(c := [c_{1}… c_{I}]^{T}\) define the observed number of phones counted in cell \(i = 1,2, \ldots, I\). Furthermore, we formalize the total number of phones across all cells by \(C := \sum_{i = 1}^{I} c_{i} = 1_{I}^{T}c\), the assumed total number of individual phones in the operating area. As in [19] \(c\) describes a cell count vector, aggregating all estimable phones around a reference time \(t^*\). In this study we ignore the time dimension and focus only on the spatial density estimation of a certain point in time.

  • Network topology data: This data source describes the configuration parameters of each relevant cell within the operating area. It can come in various levels of detail, ranging from only knowing the cell tower locations to having precise information on the azimuth direction, transmission power, beamwidth, etc. This data is used within MNO’s to improve the network via radio propagation modeling."

We plan to create one operating area, which resembles our ground truth population (GTP. Then, we create four network scenarios, which are based respectively on four network topology datasets. This in turn also creates four aggregated network event vectors (i.e., \(c\)). The remaining notebook is structured in the following way:

  • General Setup

  • Description of the operating area

  • Description of the general network topology parameters

  • Description of each network scenario

1 General Setup: Loading Packages and Custom Functions

# Data manipulation
library(tidyverse)
library(data.table) 

# Spatial operations
library(sf)
library(raster)
library(stars)

# Matrix operations
library(Matrix)

# MNO data handling and propagation model setup
# Credits to Prof. Martijn Tennekes https://github.com/mtennekes/mobloc
library(mobloc)

# Comparison of 2d histograms (Kantorovitch Wasserstein distance a.k.a. Earth Movers distance)
# Credits to Prof. Stefano Gualandi https://cran.r-project.org/web/packages/SpatialKWD/SpatialKWD.pdf
library(SpatialKWD)

# Output organisation and plotting support
library(ggthemes)
library(viridis)
library(ggrepel)
library(ggpointdensity)
library(scattermore)
library(grid)
library(gridExtra)
library(knitr)
library(DT)

# seed for reproducibility
set.seed(42)


# Loading Custom functions
source("pipeline functions.R")

2 Generation of the operating area

Our operating area is based on a semi-synthetic data generated process. For this, census data from Germany on a 100m*100m regular grid has been used, which can be downloaded here. Each element in this grid is expressed as a tile. For computation purposes only a small area of Germany was used for the toyworld, namely the area of Munich and its near surroundings. This focus area includes 160,000 tiles. The code for clipping this specific area can be found here, which is also part of this research repository. For a mobile phone population the regular census population values are used. To mimic the mobile phone population of one mobile network operator (MNO) the population is reduced to about a third.

2.1 Mobile phone population

In the following chunk we load in the focus area data and create the area object, which contains all information concerning the operating area, e.g., geographical information, mobile phone population, prior information, etc. Each object has its purpose in the remaining workflow.

# data read in
munich.raw <- readRDS("Data/munich.rds")

# define raster object from focus area and set the coordinate reference system (crs)
munich.raster <- rasterFromXYZ(munich.raw, crs = st_crs(3035)$proj4string)
## Warning in showSRID(uprojargs, format = "PROJ", multiline = "NO"): Discarded
## datum Unknown based on GRS80 ellipsoid in CRS definition
# define empty list object where all GTP objects will be stored
munich <- NULL

# define sf version of raster object
munich$area.sf.raw <- munich.raster %>%
  st_as_stars() %>%
  st_as_sf() %>%
  mutate(tile.id.chr = as.character(tile.id)) %>% 
  mutate(tile.id.fac = factor(tile.id.chr)) %>% 
  mutate(tile.id.num = as.numeric(tile.id.fac)) %>% 
  mutate(type = "NA") %>%  # only necessary if different tile types can be defined (urban, rural, etc...)
  mutate(prior.1 = 1, # uniform prior
         prior.2 = case_when(pop <= 1 ~ 0.1,
                             pop > 1 & pop <= 50 ~ 1,
                             pop > 50 ~ 100)) %>% # 3 category prior
  dplyr::select(contains("tile.id"), pop, type, elevation, contains("prior"))

# define sf version with only geometry centroids of tiles
munich$area.sf <- munich$area.sf.raw %>%
  mutate(centroid.geometry = st_centroid(.$geometry)) %>% 
  mutate(X.centroid = unlist(map(.$centroid.geometry, 1)),
         Y.centroid = unlist(map(.$centroid.geometry, 2))) 
  

# regular dataframe version
munich$area.df <- munich$area.sf %>%
  st_drop_geometry()

# prior value reduced dataframe version
munich$area.prior <- munich$area.df %>% 
  dplyr::select(tile.id.chr, contains("prior."))

# variable reduced dataframe version
munich$area.reduced.df <- munich$area.df %>% 
  dplyr::select(tile.id.chr, contains("centroid"))

# unionized version of focus area
munich$area.union <- munich$area.sf %>%
  st_union()

# bounding box coordinates of focus area
munich$area.bbox <- munich$area.union %>%
  st_bbox(crs = sf::st_crs(3035))

# specify raster object and tile id number
munich$area.raster <- munich.raster %>%
  raster(., layer = "tile.id")

# specify raster object and elevation value of each tile (here considered as constant)
munich$area.elevation <- munich.raster %>%
  raster(., layer = "elevation")

# number of tiles
munich$area.params[["tile.num"]] <- length(munich$area.df$tile.id)

# size of tiles
munich$area.params[["base.tile.size"]] <- as.numeric(sqrt(st_area(munich$area.sf[1,])))

# storing everything in area object
area <- munich

saveRDS(area, "Data/area.rds")

In the following, we can see the spatial density of the GTP (see Figure 4 in the paper).

# adjustable break points for map categories
breaks <- c(0, 2, 5, 10, 20, 50, 100, 200, 350, Inf)
# plot map and print
(GTP.spat.dens.plot <- area$area.sf %>% 
  mutate(pop.cat = cut(pop, breaks = breaks, dig.lab = 7, right = F)) %>% 
  map_density(data = ., var = "pop.cat", label = "GTP"))
Spatial density of the ground truth population

Figure 2.1: Spatial density of the ground truth population

# ggsave("Final Plots/GTP.png", GTP.spat.dens.plot, device = "png", bg = "transparent")

In the following, we can see the empirical cumulative complementary distribution (ECCDF) of the GTP (see Figure 4 in the paper). The capped empirical cumulative distribution (ECDF) is added as an insert.

# ECCDF and ECDF of GTP
(GTP.spat.dens.plot <- density_plots(area$area.df))
ECCDF and ECDF of the ground truth population, (Insert ECDF capped at 30)

Figure 2.2: ECCDF and ECDF of the ground truth population, (Insert ECDF capped at 30)

# ggsave("Final Plots/ECCDF.png", GTP.spat.dens.plot, device = "png")

# warning can be ignored

2.2 Radio network

This subsection refers to the development of a radio network within our operating area. Developing the radio network is heavily dependent on the mobloc package, which is promoted through the European Statistical System. However, we have adjusted and extended some of the functions to our needs, e.g. concerning the generation of multiple cell layers.

In general, the mobloc package allows to define many parameters, however, if they are not defined, default parameters, set by the package, are used. This makes it very easy to implement as much / as little information one has on a certain network and always getting it to work. The following adjustable parameters and default values are provided through the package for creating a radio network and modelling the signal strength in a focus area:

# possible mobloc parameters
mobloc_param()
## $W
## [1] 10
## 
## $W_small
## [1] 5
## 
## $ple
## [1] 3.75
## 
## $ple_small
## [1] 6
## 
## $ple_0
## [1] 3.5
## 
## $ple_1
## [1] 4
## 
## $midpoint
## [1] -92.5
## 
## $steepness
## [1] 0.2
## 
## $range
## [1] 10000
## 
## $range_small
## [1] 1000
## 
## $height
## [1] 30
## 
## $height_small
## [1] 8
## 
## $tilt
## [1] 5
## 
## $beam_v
## [1] 9
## 
## $beam_h
## [1] 65
## 
## $azim_dB_back
## [1] -30
## 
## $elev_dB_back
## [1] -30
## 
## $sig_d_th
## [1] 0.005
## 
## $max_overlapping_cells
## [1] 100
## 
## $TA_step
## [1] 78.12
## 
## $TA_max
## [1] 1282
## 
## $TA_buffer
## [1] 1
## 
## attr(,"class")
## [1] "mobloc_param"

For our study we construct four networks. The first two networks are one-layer networks and differ in cell density, while the other two networks are multi-layer networks. We initialize each network with the number of layers and each layer refers to a certain kind of cells (Macro, Meso and Micro). The development of each layer starts with a hexagonal grid in which the points define tower locations. The hexagons have different sizes (i.e. tower distance) dependent on the layer and may contain some randomness within the layer (jitter). Each layer spans over the complete focus area.

On each tower three directional cells are placed that are directed in a 120° angle to each other. All layers contain a rotation parameter (azimuth) to prevent cells of different layers broadcasting into the exact same direction, in reference to the operating area. In this study, we do not implement omni-directional cells, therefore, all mobloc parameters with the suffix “_small” are not used.

The cells are specified with layer specific parameters (e.g. height, power, path loss exponent, etc.). All cells of one network scenario are specified in a so-called cellplan. This cellplan is a data frame on the cell level, which contains all information on the parameters. When the cellplan is completed the cell-specific broadcasting profile (i.e. cell profile) is estimated and projected onto the operating area. The function compute_sig_strength() computes the distance, signal strength and signal dominance between any tile and any cell. Furthermore, a minimum parameter is implemented that defines the minimum signal dominance value an cell-tile relationship needs to have in order to be considered “covered”. For the generation of a network scenario, we assure that all tiles are sufficiently covered, i.e. each mobile phones within each tile have at least one cell that covers them with a signal dominance value greater than the minimum threshold, in this case 0.05.

In the following, each cellplan will be presented respectively. First, we will report some important theoretical network parameters. Then, different coverage maps will be visualized. Finally, a histogram of

2.2.1 Cellplan.1: One layer, sparse network

# specify parameters of each cell
ME.cell.param.mobloc <- mobloc_param(W = 50, range = 8000, ple = 3.7, height = 10,
                                     midpoint = -85, steepness = 0.3, sig_d_th = 0.05)

layer.list <- list(ME.cell.param.mobloc)

# create dataframe for theoretical signal strength distribution
param.df <- map_dfr(layer.list, rbind.data.frame) %>% 
  mutate(cell.kind = c("ME"),
         label = c("Meso")) %>% 
  dplyr::select(cell.kind, label, everything(), dominance.th = sig_d_th)

# reduced data frame of theoretical signal strength distribution
param.df.reduced <- param.df %>% 
  dplyr::select(cell.kind, dominance.th)

# theoretical signal strength parameter plots
sig.pram.plots <- sig_param_plots(param.df = param.df, range.max = 20000, base_size = 11)

set.seed(100)

layer.params.ext <- list(
  ME = list(tower.dist = 4100,
            rotation.deg = 35,
            jitter = 3,
            subscript = "ME",
            seed = 7,
            mobloc.params = ME.cell.param.mobloc)
  
)


cellplan.1.layer.1 <- complete_cellplan_gen(area = area,
                                            layer.params.ext = layer.params.ext,
                                            param.df = param.df)



# how many tiles are not sufficiently covered (Check)
paste0("Number of tiles which do not reach the signal dominance threshold of: " , sum(cellplan.1.layer.1$signal.dom$missing))
## [1] "Number of tiles which do not reach the signal dominance threshold of: 0"

Here we can see some important theoretical network parameters.

(cellplan.1.theo.param <- ggpubr::as_ggplot(sig.pram.plots$final))

Here we can see some important spatial network parameters.

celplan.1.spat.param <- cell.spat.diag(area = area, cellplan = cellplan.1.layer.1, label.name = "Cellplan.1")

celplan.1.spat.param$tile.coverage.hist

celplan.1.spat.param$coverage.map.complete

2.2.2 Cellplan.2: One layer, dense network

# specify parameters of each cell
ME.cell.param.mobloc <- mobloc_param(W = 50, range = 8000, ple = 3.7, height = 10,
                                     midpoint = -85, steepness = 0.3, sig_d_th = 0.05)

layer.list <- list(ME.cell.param.mobloc)

# create dataframe for theoretical signal strength distribution
param.df <- map_dfr(layer.list, rbind.data.frame) %>% 
  mutate(cell.kind = c("ME"),
         label = c("Meso")) %>% 
  dplyr::select(cell.kind, label, everything(), dominance.th = sig_d_th)

# reduced data frame of theoretical signal strength distribution
param.df.reduced <- param.df %>% 
  dplyr::select(cell.kind, dominance.th)

# theoretical signal strength parameter plots
cellplan.2.sig.pram.plots <- sig_param_plots(param.df = param.df, range.max = 20000, base_size = 11)

set.seed(100)

layer.params.ext <- list(
  ME = list(tower.dist = 3000,
            rotation.deg = 35,
            jitter = 1,
            subscript = "ME",
            seed = 7,
            mobloc.params = ME.cell.param.mobloc)
  
)


cellplan.1.layer.2 <- complete_cellplan_gen(area = area,
                                            layer.params.ext = layer.params.ext,
                                            param.df = param.df)



# how many tiles are not sufficiently covered
paste0("Number of tiles which do not reach the signal dominance threshold of: " , sum(cellplan.1.layer.2$signal.dom$missing))
## [1] "Number of tiles which do not reach the signal dominance threshold of: 0"

Here we can see some important theoretical network parameters.

(cellplan.2.theo.param <- ggpubr::as_ggplot(cellplan.2.sig.pram.plots$final))

Here we can see some important spatial network parameters.

celplan.2.spat.param <- cell.spat.diag(area = area, cellplan = cellplan.1.layer.2, label.name = "Cellplan.2")

celplan.2.spat.param$tile.coverage.hist

celplan.2.spat.param$coverage.map.complete

2.2.3 Cellplan.3: Three layer network

# specify parameters of each cell
MA.cell.param.mobloc <- mobloc_param(W = 5, # Power in Watts
                                     range = 10000, # maximum coverage range
                                     ple = 3.4, # Path loss exponent
                                     height = 10, # height of the antenna
                                     midpoint = -85, # midpoint parameter of the logistic function for signal dominance
                                     steepness = 0.15, # steepness parameter of the logistic function for signal dominance
                                     sig_d_th = 0.05) # dominance minimum threshold 

ME.cell.param.mobloc <- mobloc_param(W = 50, range = 3500, ple = 3.8, height = 10,
                                     midpoint = -85, steepness = 0.3, sig_d_th = 0.05)

MI.cell.param.mobloc <- mobloc_param(W = 1, range = 3500, ple = 4, height = 6,
                                     midpoint = -85, steepness = 0.4, sig_d_th = 0.05)


layer.list <- list(MA.cell.param.mobloc, ME.cell.param.mobloc, MI.cell.param.mobloc)

# create dataframe for theoretical signal strength distribution
param.df <- map_dfr(layer.list, rbind.data.frame) %>% 
  mutate(cell.kind = c("MA", "ME", "MI"),
         label = c("Macro", "Meso", "Micro")) %>% 
  dplyr::select(cell.kind, label, everything(), dominance.th = sig_d_th)

# reduced data frame of theoretical signal strength distribution
param.df.reduced <- param.df %>% 
  dplyr::select(cell.kind, dominance.th)

# theoretical signal strength parameter plots
cellplan.3.sig.pram.plots <- sig_param_plots(param.df = param.df, range.max = 20000, base_size = 11)
## Warning: Using alpha for a discrete variable is not advised.

## Warning: Using alpha for a discrete variable is not advised.
set.seed(100)

layer.params.ext <- list(
  MA = list(tower.dist = 8500,
            rotation.deg = 0,
            jitter = 1000,
            subscript = "MA",
            seed = 3,
            mobloc.params = MA.cell.param.mobloc),
  ME = list(tower.dist = 3500,
            rotation.deg = 35,
            jitter = 700,
            subscript = "ME",
            seed = 7,
            mobloc.params = ME.cell.param.mobloc),
  MI = list(tower.dist = 10000,
            rotation.deg = 60,
            jitter = 2000,
            subscript = "MI",
            seed = 10,
            mobloc.params = MI.cell.param.mobloc)
  
)


cellplan.3.layer <- complete_cellplan_gen(area = area,
                                          layer.params.ext = layer.params.ext,
                                          param.df = param.df)
## Warning: attribute variables are assumed to be spatially constant throughout all
## geometries
## Warning: attribute variables are assumed to be spatially constant throughout all
## geometries
## Warning: Unknown or uninitialised column: `height`.
## Warning: Unknown or uninitialised column: `W`.
## Warning: Unknown or uninitialised column: `range`.
## Warning: Unknown or uninitialised column: `ple`.
## Warning: attribute variables are assumed to be spatially constant throughout all
## geometries

## Warning: attribute variables are assumed to be spatially constant throughout all
## geometries
## Warning: Unknown or uninitialised column: `height`.
## Warning: Unknown or uninitialised column: `W`.
## Warning: Unknown or uninitialised column: `range`.
## Warning: Unknown or uninitialised column: `ple`.
## Warning: attribute variables are assumed to be spatially constant throughout all
## geometries

## Warning: attribute variables are assumed to be spatially constant throughout all
## geometries
## Warning: Unknown or uninitialised column: `height`.
## Warning: Unknown or uninitialised column: `W`.
## Warning: Unknown or uninitialised column: `range`.
## Warning: Unknown or uninitialised column: `ple`.
# how many tiles are not sufficiently covered
paste0("Number of tiles which do not reach the signal dominance threshold of: " , sum(cellplan.3.layer$signal.dom$missing))
## [1] "Number of tiles which do not reach the signal dominance threshold of: 0"

Here we can see some important theoretical network parameters.

(cellplan.3.theo.param <- ggpubr::as_ggplot(cellplan.3.sig.pram.plots$final))

Here we can see some important spatial network parameters.

celplan.3.spat.param <- cell.spat.diag(area = area, cellplan = cellplan.3.layer, label.name = "Cellplan.3")

celplan.3.spat.param$tile.coverage.hist

celplan.3.spat.param$coverage.map.complete

2.2.4 Cellplan.4: Two layer network

# specify parameters of each cell
ME.cell.param.mobloc <- mobloc_param(W = 50, range = 8000, ple = 3.7, height = 10,
                                     midpoint = -85, steepness = 0.3, sig_d_th = 0.05)

MI.cell.param.mobloc <- mobloc_param(W = 1, range = 3500, ple = 4, height = 6,
                                     midpoint = -85, steepness = 0.4, sig_d_th = 0.05)

layer.list <- list(ME.cell.param.mobloc, MI.cell.param.mobloc)

# create dataframe for theoretical signal strength distribution
param.df <- map_dfr(layer.list, rbind.data.frame) %>% 
  mutate(cell.kind = c("ME", "MI"),
         label = c("Meso", "Micro")) %>% 
  dplyr::select(cell.kind, label, everything(), dominance.th = sig_d_th)

# reduced data frame of theoretical signal strength distribution
param.df.reduced <- param.df %>% 
  dplyr::select(cell.kind, dominance.th)

# theoretical signal strength parameter plots
cellplan.4.sig.pram.plots <- sig_param_plots(param.df = param.df, range.max = 20000, base_size = 11)
## Warning: Using alpha for a discrete variable is not advised.

## Warning: Using alpha for a discrete variable is not advised.
set.seed(100)

layer.params.ext <- list(
    ME = list(tower.dist = 4000,
            rotation.deg = 35,
            jitter = 700,
            subscript = "ME",
            seed = 7,
            mobloc.params = ME.cell.param.mobloc),
    MI = list(tower.dist = 10000,
            rotation.deg = 60,
            jitter = 2000,
            subscript = "MI",
            seed = 10,
            mobloc.params = MI.cell.param.mobloc)
  
)


cellplan.2.layer <- complete_cellplan_gen(area = area,
                                          layer.params.ext = layer.params.ext,
                                          param.df = param.df)
## Warning: attribute variables are assumed to be spatially constant throughout all
## geometries
## Warning: attribute variables are assumed to be spatially constant throughout all
## geometries
## Warning: Unknown or uninitialised column: `height`.
## Warning: Unknown or uninitialised column: `W`.
## Warning: Unknown or uninitialised column: `range`.
## Warning: Unknown or uninitialised column: `ple`.
## Warning: attribute variables are assumed to be spatially constant throughout all
## geometries

## Warning: attribute variables are assumed to be spatially constant throughout all
## geometries
## Warning: Unknown or uninitialised column: `height`.
## Warning: Unknown or uninitialised column: `W`.
## Warning: Unknown or uninitialised column: `range`.
## Warning: Unknown or uninitialised column: `ple`.
# how many tiles are not sufficiently covered
paste0("Number of tiles which do not reach the signal dominance threshold of: " , sum(cellplan.2.layer$signal.dom$missing))
## [1] "Number of tiles which do not reach the signal dominance threshold of: 0"

Here we can see some important theoretical network parameters.

(cellplan.4.theo.param <- ggpubr::as_ggplot(cellplan.4.sig.pram.plots$final))

Here we can see some important spatial network parameters.

celplan.4.spat.param <- cell.spat.diag(area = area, cellplan = cellplan.2.layer, label.name = "Cellplan.4")

celplan.4.spat.param$tile.coverage.hist

celplan.4.spat.param$coverage.map.complete

After this presentation of each cellplan we will perform now the phone-to-cell association, which is responsible for creating each vector \(c\).

2.3 Device-to-cell association (Generative model)

Here we bring the mobile phone population with each network together and stochastically assign the mobile phone population in each tile to the respective cells. The result is the so-called c-vector (i.e., \(c\)), which describes the number of mobile phones assigned to each cell. The basis parameter for this assignment is the signal dominance which is normalized in the form of a conditional probability. These conditional probabilities (emission probabilities) describe the elements of \(P\).

First, we combine and save all cellplan objects into the list object cellplans.list, which helps with automatizing each phone-to-cell association task.

cellplans.list <- list(
  cellplan.1 = cellplan.1.layer.1,
  cellplan.2 = cellplan.1.layer.2,
  cellplan.3 = cellplan.3.layer,
  cellplan.4 = cellplan.2.layer
)

# saveRDS(cellplans.list, "Data/cellplans.list.rds")

Then, we perform the phone-to-cell association. Finally, we save the object gen.model.objects, which will contain three objects for further use in the workflow:

  • C.vec.df.list: This object contains the c-vector for each network scenario.

  • P.long.complete.df: This object contains the for each network scenario the tile-cell relationships (edge list), which are characterized by the variables emission probabilities (generative model), signal strength and signal dominance values. Note, this object contains also the “zero” elements, meaning tile-cell relationships that have an emission probability of 0. Also, it contains certain cellplan parameters, making it a very large dataframe for each network scenario.

  • P.long.df: This object is a reduced version of P.long.complete.df, without zero elements and no cellplan parameters.

# specify the option of differing parameters for sig_d_th and max_overlapping_cells depending on the cell type in custom create_strength_llh function

# Workaround: securing that sig_d_th and max_overlapping_cells are the same for each layer
signal.strength.llh.param <- list(sig_d_th = 0.05,
                                  max_overlapping_cells = 100)

# define connection llh and classify tiles depending on coverage status
connection.llh.list <- map(cellplans.list, ~create_strength_llh_custom(signal.strength.comb.dt = .x$signal.strength.comb.dt,
                                                                       signal.strength.llh.param = signal.strength.llh.param,
                                                                       smart.rounding.digits = 3,
                                                                       area.df = area$area.df))
## Number of tiles which are unsufficiently covered: 0Number of tiles which are unsufficiently covered: 0Number of tiles which are unsufficiently covered: 0Number of tiles which are unsufficiently covered: 0
# create c-vector
C.vec.df.list <- map(connection.llh.list, ~create_c_vector(signal.strength.llh.combined = .x$signal.strength.llh.combined))

# develop long format of P matrix which also contains certain cellplan parameters and "zero elements"
# develop different versions of id variables (tile and antennas) for easier joining
P.long.complete.df <- map2(connection.llh.list, C.vec.df.list, ~full_join(.x$signal.strength.llh.combined, .y, by = "cell")) %>%
  map(~dplyr::select(., tile.id.chr,, tile.id.fac, tile.id.num, pop, cell, type, dist, pij, phones.sum)) %>%
  map(~mutate(., cell.chr = as.character(cell))) %>%
  map2(cellplans.list, ~mutate(.x, cell.fac = factor(cell.chr, levels = fct_unique(.y$cellplan.combined.df$cell.fac)))) %>%
  map(~mutate(., cell.num = as.numeric(cell.fac)))

# Long format of P matrix with minimal variables and and unique rows
P.long.df <- P.long.complete.df %>%
  map(~dplyr::select(., tile.id.chr, tile.id.fac, tile.id.num, cell.chr, cell.fac, cell.num, pij)) %>%
  map(~distinct(.))


gen.model.objects <- list(C.vec.df.list = C.vec.df.list,
                          P.long.complete.df = P.long.complete.df,
                          P.long.df = P.long.df)

# saveRDS(gen.model.objects, "Data/gen.model.objects.rds")
sessionInfo()
## R version 4.0.5 (2021-03-31)
## Platform: x86_64-w64-mingw32/x64 (64-bit)
## Running under: Windows 10 x64 (build 19043)
## 
## Matrix products: default
## 
## locale:
## [1] LC_COLLATE=German_Germany.1252  LC_CTYPE=German_Germany.1252   
## [3] LC_MONETARY=German_Germany.1252 LC_NUMERIC=C                   
## [5] LC_TIME=German_Germany.1252    
## 
## attached base packages:
## [1] grid      stats     graphics  grDevices utils     datasets  methods  
## [8] base     
## 
## other attached packages:
##  [1] DT_0.14              knitr_1.34           gridExtra_2.3       
##  [4] scattermore_0.7      ggpointdensity_0.1.0 ggrepel_0.8.2       
##  [7] viridis_0.5.1        viridisLite_0.3.0    ggthemes_4.2.0      
## [10] SpatialKWD_0.4.0     mobloc_0.5-1         Matrix_1.3-2        
## [13] stars_0.5-2          abind_1.4-5          raster_3.3-7        
## [16] sp_1.4-2             sf_1.0-3             data.table_1.13.2   
## [19] forcats_0.5.1        stringr_1.4.0        dplyr_1.0.5         
## [22] purrr_0.3.4          readr_1.4.0          tidyr_1.1.3         
## [25] tibble_3.1.2         ggplot2_3.3.3        tidyverse_1.3.1     
## 
## loaded via a namespace (and not attached):
##  [1] fs_1.5.0           lubridate_1.7.10   doParallel_1.0.16  httr_1.4.2        
##  [5] tools_4.0.5        backports_1.1.7    utf8_1.2.1         rgdal_1.5-12      
##  [9] R6_2.5.0           KernSmooth_2.23-18 DBI_1.1.1          colorspace_1.4-1  
## [13] withr_2.4.1        tidyselect_1.1.0   curl_4.3           compiler_4.0.5    
## [17] cli_2.5.0          rvest_1.0.0        xml2_1.3.2         labeling_0.4.2    
## [21] bookdown_0.21      scales_1.1.1       classInt_0.4-3     digest_0.6.25     
## [25] foreign_0.8-81     rmarkdown_2.5      rio_0.5.16         pkgconfig_2.0.3   
## [29] htmltools_0.5.1.1  highr_0.8          dbplyr_2.1.1       htmlwidgets_1.5.3 
## [33] rlang_0.4.10       readxl_1.3.1       rstudioapi_0.13    generics_0.1.0    
## [37] farver_2.0.3       jsonlite_1.7.2     zip_2.2.0          car_3.0-8         
## [41] magrittr_2.0.1     Rcpp_1.0.7         munsell_0.5.0      fansi_0.4.1       
## [45] lifecycle_1.0.0    stringi_1.4.6      yaml_2.2.1         carData_3.0-4     
## [49] parallel_4.0.5     crayon_1.4.1       lattice_0.20-41    cowplot_1.0.0     
## [53] haven_2.3.1        hms_1.1.0          pillar_1.6.1       ggpubr_0.4.0      
## [57] ggsignif_0.6.0     codetools_0.2-18   reprex_2.0.0       glue_1.4.1        
## [61] evaluate_0.14      modelr_0.1.8       vctrs_0.3.8        foreach_1.5.1     
## [65] cellranger_1.1.0   gtable_0.3.0       assertthat_0.2.1   openxlsx_4.1.5    
## [69] xfun_0.26          lwgeom_0.2-5       broom_0.7.6        e1071_1.7-3       
## [73] rstatix_0.6.0      class_7.3-18       iterators_1.0.13   units_0.6-7       
## [77] ellipsis_0.3.2
LS0tDQp0aXRsZTogIk1OT191bmNlcnRhaW50eTogR2VuZXJhdGlvbiBvZiBuZXR3b3JrIHNjZW5hcmlvcyINCmF1dGhvcjogIk1hcmNvIFJhbWxqYWsiDQpkYXRlOiAiYHIgU3lzLkRhdGUoKWAiDQpvdXRwdXQ6DQogIGJvb2tkb3duOjpodG1sX2RvY3VtZW50MjoNCiAgICBjb2RlX2ZvbGRpbmc6IHNob3cNCiAgICBjb2RlX2Rvd25sb2FkOiB0cnVlDQogICAgdGhlbWU6IHNhbmRzdG9uZQ0KICAgIHRvYzogdHJ1ZQ0KICAgIHRvY19mbG9hdDogdHJ1ZQ0KICAgIG51bWJlcl9zZWN0aW9uczogdHJ1ZQ0KICAgIGZpZ19jYXB0aW9uOiB5ZXMNCmtuaXQ6IChmdW5jdGlvbihpbnB1dF9maWxlLCBlbmNvZGluZykgeyBvdXRfZGlyIDwtICdkb2NzJzsgcm1hcmtkb3duOjpyZW5kZXIoaW5wdXRfZmlsZSwNCiAgZW5jb2Rpbmc9ZW5jb2RpbmcsIG91dHB1dF9maWxlPWZpbGUucGF0aChkaXJuYW1lKGlucHV0X2ZpbGUpLCBvdXRfZGlyLCAnaW5kZXguaHRtbCcpKX0pDQotLS0NCg0KYGBgez1odG1sfQ0KPHN0eWxlPg0KICBwLmNhcHRpb24gew0KICBmb250LXNpemU6IDFlbTsNCn0NCiAgYm9keSB7DQogIGZvbnQtc2l6ZTogMTFwdDsNCn0NCjwvc3R5bGU+DQpgYGANClRoaXMgbm90ZWJvb2sgcHJvdmlkZXMgKipzdXBwbGVtZW50YXJ5IG1hdGVyaWFsKiogZm9yIG15IHRoZXNpcyBwcm9qZWN0IHRpdGxlZDogIioqVGFja2xpbmcgVW5jZXJ0YWludHkgaW4gdGhlIFNwYXRpYWwgRGVuc2l0eSBFc3RpbWF0aW9uIGZyb20gTW9iaWxlIE5ldHdvcmsgT3BlcmF0b3IgZGF0YSoqIi4gSXQgZm9jdXNlcyBvbiB0aGUgZmlyc3QgbW9kdWxlIG9mIHRoZSBNTk8tc2ltdWxhdG9yIHdvcmtmbG93LCB0aGUgZ2VuZXJhdGlvbiBvZiBuZXR3b3JrIHNjZW5hcmlvcy4gSXRzIGdvYWwgaXMgdG8gcHJvdmlkZSB0aGUgY29tcGxldGUgY29kZSBmb3IgcmVwcm9kdWN0aW9uIGFuZCBpbi1kZXB0aCBhbmFseXNpcyBvZiB0aGUgZm91ciBkaWZmZXJlbnQgbmV0d29yayBzY2VuYXJpb3MuIEZ1cnRoZXIgaW5mb3JtYXRpb24gb2YgdGhpcyBwcm9qZWN0IGNhbiBiZSBmb3VuZCBbaGVyZV0oaHR0cHM6Ly9naXRodWIuY29tL1ItcmFtbGphay9NTk9fdW5jZXJ0YWludHkpLg0KDQpBcyBtZW50aW9uZWQgb24gcGFnZSA0IG9mIHRoZSBbdGhlc2lzIGFydGljbGVdKGh0dHBzOi8vZ2l0aHViLmNvbS9SLXJhbWxqYWsvTU5PX3VuY2VydGFpbnR5L2Jsb2IvbWFpbi9EaXNzZW1pbmF0aW9uL1RoZXNpcyUyME1hbnVzY3JpcHQvdGhlc2lzJTIwbWFyY28lMjByYW1samFrLnBkZiksIHdlIG5lZWQgdG8gY3JlYXRlIHRocmVlIGRhdGEgc291cmNlczoNCg0KLSAgICJPcGVyYXRpbmcgYXJlYSBkYXRhOiBXZSBhc3N1bWUgdGhlIG9wZXJhdGluZyBhcmVhIHRvIGJlIGRpc2NyZXRpemVkIGludG8gYSByZWd1bGFyIGdyaWQsIGUuZy4sIDEwMG0geCAxMDBtLiBFYWNoIHNxdWFyZSB1bml0IGlzIHRlcm1lZCBhcyBhIHRpbGUsIGluZGV4ZWQgaW4gJGogPSAxLDIsIFxsZG90cywgSiQuICR1X2okIGRlbm90ZXMgdGhlIHVua25vd24gbm9uLW5lZ2F0aXZlIG51bWJlciBvZiBtb2JpbGUgcGhvbmVzIG9mIHRoZSAkaiR0aCB0aWxlLCBzdG9yZWQgaW4gdGhlIGNvbHVtbiB2ZWN0b3IgJHUgOj0gW3VfezF94oCmIHVfe0p9XV57VH0kLiBUaGlzIHN0YW5kYXJkaXplZCB1bml0IGhlbHBzIHVzIHNldCBhIGZvcm1hbGlzbSBmcmFtZXdvcmsgdGhhdCBoYXMgYmVlbiB1c2VkIGluIG11bHRpcGxlIHN0dWRpZXMgYmVmb3JlaGFuZCBbMTksIDIyLCAyOF0uDQoNCi0gICBBZ2dyZWdhdGVkIG5ldHdvcmsgZXZlbnQgZGF0YTogTGV0IHRoZSAkaSR0aCBlbGVtZW50ICRjX3tpfSQgb2YgdGhlIGNvbHVtbiB2ZWN0b3IgJGMgOj0gW2NfezF94oCmIGNfe0l9XV57VH0kIGRlZmluZSB0aGUgb2JzZXJ2ZWQgbnVtYmVyIG9mIHBob25lcyBjb3VudGVkIGluIGNlbGwgJGkgPSAxLDIsIFxsZG90cywgSSQuIEZ1cnRoZXJtb3JlLCB3ZSBmb3JtYWxpemUgdGhlIHRvdGFsIG51bWJlciBvZiBwaG9uZXMgYWNyb3NzIGFsbCBjZWxscyBieSAkQyA6PSBcc3VtX3tpID0gMX1ee0l9IGNfe2l9ID0gMV97SX1ee1R9YyQsIHRoZSBhc3N1bWVkIHRvdGFsIG51bWJlciBvZiBpbmRpdmlkdWFsIHBob25lcyBpbiB0aGUgb3BlcmF0aW5nIGFyZWEuIEFzIGluIFsxOV0gJGMkIGRlc2NyaWJlcyBhIGNlbGwgY291bnQgdmVjdG9yLCBhZ2dyZWdhdGluZyBhbGwgZXN0aW1hYmxlIHBob25lcyBhcm91bmQgYSByZWZlcmVuY2UgdGltZSAkdF4qJC4gSW4gdGhpcyBzdHVkeSB3ZSBpZ25vcmUgdGhlIHRpbWUgZGltZW5zaW9uIGFuZCBmb2N1cyBvbmx5IG9uIHRoZSBzcGF0aWFsIGRlbnNpdHkgZXN0aW1hdGlvbiBvZiBhIGNlcnRhaW4gcG9pbnQgaW4gdGltZS4NCg0KLSAgIE5ldHdvcmsgdG9wb2xvZ3kgZGF0YTogVGhpcyBkYXRhIHNvdXJjZSBkZXNjcmliZXMgdGhlIGNvbmZpZ3VyYXRpb24gcGFyYW1ldGVycyBvZiBlYWNoIHJlbGV2YW50IGNlbGwgd2l0aGluIHRoZSBvcGVyYXRpbmcgYXJlYS4gSXQgY2FuIGNvbWUgaW4gdmFyaW91cyBsZXZlbHMgb2YgZGV0YWlsLCByYW5naW5nIGZyb20gb25seSBrbm93aW5nIHRoZSBjZWxsIHRvd2VyIGxvY2F0aW9ucyB0byBoYXZpbmcgcHJlY2lzZSBpbmZvcm1hdGlvbiBvbiB0aGUgYXppbXV0aCBkaXJlY3Rpb24sIHRyYW5zbWlzc2lvbiBwb3dlciwgYmVhbXdpZHRoLCBldGMuIFRoaXMgZGF0YSBpcyB1c2VkIHdpdGhpbiBNTk8ncyB0byBpbXByb3ZlIHRoZSBuZXR3b3JrIHZpYSByYWRpbyBwcm9wYWdhdGlvbiBtb2RlbGluZy4iDQoNCldlIHBsYW4gdG8gY3JlYXRlIG9uZSBvcGVyYXRpbmcgYXJlYSwgd2hpY2ggcmVzZW1ibGVzIG91ciBncm91bmQgdHJ1dGggcG9wdWxhdGlvbiAoR1RQLiBUaGVuLCB3ZSBjcmVhdGUgZm91ciBuZXR3b3JrIHNjZW5hcmlvcywgd2hpY2ggYXJlIGJhc2VkIHJlc3BlY3RpdmVseSBvbiBmb3VyIG5ldHdvcmsgdG9wb2xvZ3kgZGF0YXNldHMuIFRoaXMgaW4gdHVybiBhbHNvIGNyZWF0ZXMgZm91ciBhZ2dyZWdhdGVkIG5ldHdvcmsgZXZlbnQgdmVjdG9ycyAoaS5lLiwgJGMkKS4gVGhlIHJlbWFpbmluZyBub3RlYm9vayBpcyBzdHJ1Y3R1cmVkIGluIHRoZSBmb2xsb3dpbmcgd2F5Og0KDQotICAgR2VuZXJhbCBTZXR1cA0KDQotICAgRGVzY3JpcHRpb24gb2YgdGhlIG9wZXJhdGluZyBhcmVhDQoNCi0gICBEZXNjcmlwdGlvbiBvZiB0aGUgZ2VuZXJhbCBuZXR3b3JrIHRvcG9sb2d5IHBhcmFtZXRlcnMNCg0KLSAgIERlc2NyaXB0aW9uIG9mIGVhY2ggbmV0d29yayBzY2VuYXJpbw0KDQojIEdlbmVyYWwgU2V0dXA6IExvYWRpbmcgUGFja2FnZXMgYW5kIEN1c3RvbSBGdW5jdGlvbnMNCg0KYGBge3IgcGFja2FnZXMsIG1lc3NhZ2U9RkFMU0V9DQojIERhdGEgbWFuaXB1bGF0aW9uDQpsaWJyYXJ5KHRpZHl2ZXJzZSkNCmxpYnJhcnkoZGF0YS50YWJsZSkgDQoNCiMgU3BhdGlhbCBvcGVyYXRpb25zDQpsaWJyYXJ5KHNmKQ0KbGlicmFyeShyYXN0ZXIpDQpsaWJyYXJ5KHN0YXJzKQ0KDQojIE1hdHJpeCBvcGVyYXRpb25zDQpsaWJyYXJ5KE1hdHJpeCkNCg0KIyBNTk8gZGF0YSBoYW5kbGluZyBhbmQgcHJvcGFnYXRpb24gbW9kZWwgc2V0dXANCiMgQ3JlZGl0cyB0byBQcm9mLiBNYXJ0aWpuIFRlbm5la2VzIGh0dHBzOi8vZ2l0aHViLmNvbS9tdGVubmVrZXMvbW9ibG9jDQpsaWJyYXJ5KG1vYmxvYykNCg0KIyBDb21wYXJpc29uIG9mIDJkIGhpc3RvZ3JhbXMgKEthbnRvcm92aXRjaCBXYXNzZXJzdGVpbiBkaXN0YW5jZSBhLmsuYS4gRWFydGggTW92ZXJzIGRpc3RhbmNlKQ0KIyBDcmVkaXRzIHRvIFByb2YuIFN0ZWZhbm8gR3VhbGFuZGkgaHR0cHM6Ly9jcmFuLnItcHJvamVjdC5vcmcvd2ViL3BhY2thZ2VzL1NwYXRpYWxLV0QvU3BhdGlhbEtXRC5wZGYNCmxpYnJhcnkoU3BhdGlhbEtXRCkNCg0KIyBPdXRwdXQgb3JnYW5pc2F0aW9uIGFuZCBwbG90dGluZyBzdXBwb3J0DQpsaWJyYXJ5KGdndGhlbWVzKQ0KbGlicmFyeSh2aXJpZGlzKQ0KbGlicmFyeShnZ3JlcGVsKQ0KbGlicmFyeShnZ3BvaW50ZGVuc2l0eSkNCmxpYnJhcnkoc2NhdHRlcm1vcmUpDQpsaWJyYXJ5KGdyaWQpDQpsaWJyYXJ5KGdyaWRFeHRyYSkNCmxpYnJhcnkoa25pdHIpDQpsaWJyYXJ5KERUKQ0KDQojIHNlZWQgZm9yIHJlcHJvZHVjaWJpbGl0eQ0Kc2V0LnNlZWQoNDIpDQoNCg0KIyBMb2FkaW5nIEN1c3RvbSBmdW5jdGlvbnMNCnNvdXJjZSgicGlwZWxpbmUgZnVuY3Rpb25zLlIiKQ0KDQpgYGANCg0KIyBHZW5lcmF0aW9uIG9mIHRoZSBvcGVyYXRpbmcgYXJlYQ0KDQpPdXIgb3BlcmF0aW5nIGFyZWEgaXMgYmFzZWQgb24gYSBzZW1pLXN5bnRoZXRpYyBkYXRhIGdlbmVyYXRlZCBwcm9jZXNzLiBGb3IgdGhpcywgY2Vuc3VzIGRhdGEgZnJvbSBHZXJtYW55IG9uIGEgMTAwbVwqMTAwbSByZWd1bGFyIGdyaWQgaGFzIGJlZW4gdXNlZCwgd2hpY2ggY2FuIGJlIGRvd25sb2FkZWQgW2hlcmVdKGh0dHBzOi8vd3d3LnplbnN1czIwMTEuZGUvREUvSG9tZS9Ba3R1ZWxsZXMvRGVtb2dyYWZpc2NoZUdydW5kZGF0ZW4uaHRtbD9ubj0zMDY1NDc0KS4gRWFjaCBlbGVtZW50IGluIHRoaXMgZ3JpZCBpcyBleHByZXNzZWQgYXMgYSB0aWxlLiBGb3IgY29tcHV0YXRpb24gcHVycG9zZXMgb25seSBhIHNtYWxsIGFyZWEgb2YgR2VybWFueSB3YXMgdXNlZCBmb3IgdGhlIHRveXdvcmxkLCBuYW1lbHkgdGhlIGFyZWEgb2YgTXVuaWNoIGFuZCBpdHMgbmVhciBzdXJyb3VuZGluZ3MuIFRoaXMgZm9jdXMgYXJlYSBpbmNsdWRlcyAxNjAsMDAwIHRpbGVzLiBUaGUgY29kZSBmb3IgY2xpcHBpbmcgdGhpcyBzcGVjaWZpYyBhcmVhIGNhbiBiZSBmb3VuZCBbaGVyZV0oaHR0cHM6Ly9naXRodWIuY29tL1ItcmFtbGphay9NTk9fbW9iZGVuc2l0eS9ibG9iL21hc3Rlci9Db2RlL011bmljaF9nZW5lcmF0ZS5SKSwgd2hpY2ggaXMgYWxzbyBwYXJ0IG9mIHRoaXMgcmVzZWFyY2ggcmVwb3NpdG9yeS4gRm9yIGEgbW9iaWxlIHBob25lIHBvcHVsYXRpb24gdGhlIHJlZ3VsYXIgY2Vuc3VzIHBvcHVsYXRpb24gdmFsdWVzIGFyZSB1c2VkLiBUbyBtaW1pYyB0aGUgbW9iaWxlIHBob25lIHBvcHVsYXRpb24gb2Ygb25lIG1vYmlsZSBuZXR3b3JrIG9wZXJhdG9yIChNTk8pIHRoZSBwb3B1bGF0aW9uIGlzIHJlZHVjZWQgdG8gYWJvdXQgYSB0aGlyZC4NCg0KIyMgTW9iaWxlIHBob25lIHBvcHVsYXRpb24NCg0KSW4gdGhlIGZvbGxvd2luZyBjaHVuayB3ZSBsb2FkIGluIHRoZSBmb2N1cyBhcmVhIGRhdGEgYW5kIGNyZWF0ZSB0aGUgYGFyZWFgIG9iamVjdCwgd2hpY2ggY29udGFpbnMgYWxsIGluZm9ybWF0aW9uIGNvbmNlcm5pbmcgdGhlIG9wZXJhdGluZyBhcmVhLCBlLmcuLCBnZW9ncmFwaGljYWwgaW5mb3JtYXRpb24sIG1vYmlsZSBwaG9uZSBwb3B1bGF0aW9uLCBwcmlvciBpbmZvcm1hdGlvbiwgZXRjLiBFYWNoIG9iamVjdCBoYXMgaXRzIHB1cnBvc2UgaW4gdGhlIHJlbWFpbmluZyB3b3JrZmxvdy4NCg0KYGBge3IgcG9wdWxhdGlvbi1kYXRhfQ0KIyBkYXRhIHJlYWQgaW4NCm11bmljaC5yYXcgPC0gcmVhZFJEUygiRGF0YS9tdW5pY2gucmRzIikNCg0KIyBkZWZpbmUgcmFzdGVyIG9iamVjdCBmcm9tIGZvY3VzIGFyZWEgYW5kIHNldCB0aGUgY29vcmRpbmF0ZSByZWZlcmVuY2Ugc3lzdGVtIChjcnMpDQptdW5pY2gucmFzdGVyIDwtIHJhc3RlckZyb21YWVoobXVuaWNoLnJhdywgY3JzID0gc3RfY3JzKDMwMzUpJHByb2o0c3RyaW5nKQ0KDQojIGRlZmluZSBlbXB0eSBsaXN0IG9iamVjdCB3aGVyZSBhbGwgR1RQIG9iamVjdHMgd2lsbCBiZSBzdG9yZWQNCm11bmljaCA8LSBOVUxMDQoNCiMgZGVmaW5lIHNmIHZlcnNpb24gb2YgcmFzdGVyIG9iamVjdA0KbXVuaWNoJGFyZWEuc2YucmF3IDwtIG11bmljaC5yYXN0ZXIgJT4lDQogIHN0X2FzX3N0YXJzKCkgJT4lDQogIHN0X2FzX3NmKCkgJT4lDQogIG11dGF0ZSh0aWxlLmlkLmNociA9IGFzLmNoYXJhY3Rlcih0aWxlLmlkKSkgJT4lIA0KICBtdXRhdGUodGlsZS5pZC5mYWMgPSBmYWN0b3IodGlsZS5pZC5jaHIpKSAlPiUgDQogIG11dGF0ZSh0aWxlLmlkLm51bSA9IGFzLm51bWVyaWModGlsZS5pZC5mYWMpKSAlPiUgDQogIG11dGF0ZSh0eXBlID0gIk5BIikgJT4lICAjIG9ubHkgbmVjZXNzYXJ5IGlmIGRpZmZlcmVudCB0aWxlIHR5cGVzIGNhbiBiZSBkZWZpbmVkICh1cmJhbiwgcnVyYWwsIGV0Yy4uLikNCiAgbXV0YXRlKHByaW9yLjEgPSAxLCAjIHVuaWZvcm0gcHJpb3INCiAgICAgICAgIHByaW9yLjIgPSBjYXNlX3doZW4ocG9wIDw9IDEgfiAwLjEsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgIHBvcCA+IDEgJiBwb3AgPD0gNTAgfiAxLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwb3AgPiA1MCB+IDEwMCkpICU+JSAjIDMgY2F0ZWdvcnkgcHJpb3INCiAgZHBseXI6OnNlbGVjdChjb250YWlucygidGlsZS5pZCIpLCBwb3AsIHR5cGUsIGVsZXZhdGlvbiwgY29udGFpbnMoInByaW9yIikpDQoNCiMgZGVmaW5lIHNmIHZlcnNpb24gd2l0aCBvbmx5IGdlb21ldHJ5IGNlbnRyb2lkcyBvZiB0aWxlcw0KbXVuaWNoJGFyZWEuc2YgPC0gbXVuaWNoJGFyZWEuc2YucmF3ICU+JQ0KICBtdXRhdGUoY2VudHJvaWQuZ2VvbWV0cnkgPSBzdF9jZW50cm9pZCguJGdlb21ldHJ5KSkgJT4lIA0KICBtdXRhdGUoWC5jZW50cm9pZCA9IHVubGlzdChtYXAoLiRjZW50cm9pZC5nZW9tZXRyeSwgMSkpLA0KICAgICAgICAgWS5jZW50cm9pZCA9IHVubGlzdChtYXAoLiRjZW50cm9pZC5nZW9tZXRyeSwgMikpKSANCiAgDQoNCiMgcmVndWxhciBkYXRhZnJhbWUgdmVyc2lvbg0KbXVuaWNoJGFyZWEuZGYgPC0gbXVuaWNoJGFyZWEuc2YgJT4lDQogIHN0X2Ryb3BfZ2VvbWV0cnkoKQ0KDQojIHByaW9yIHZhbHVlIHJlZHVjZWQgZGF0YWZyYW1lIHZlcnNpb24NCm11bmljaCRhcmVhLnByaW9yIDwtIG11bmljaCRhcmVhLmRmICU+JSANCiAgZHBseXI6OnNlbGVjdCh0aWxlLmlkLmNociwgY29udGFpbnMoInByaW9yLiIpKQ0KDQojIHZhcmlhYmxlIHJlZHVjZWQgZGF0YWZyYW1lIHZlcnNpb24NCm11bmljaCRhcmVhLnJlZHVjZWQuZGYgPC0gbXVuaWNoJGFyZWEuZGYgJT4lIA0KICBkcGx5cjo6c2VsZWN0KHRpbGUuaWQuY2hyLCBjb250YWlucygiY2VudHJvaWQiKSkNCg0KIyB1bmlvbml6ZWQgdmVyc2lvbiBvZiBmb2N1cyBhcmVhDQptdW5pY2gkYXJlYS51bmlvbiA8LSBtdW5pY2gkYXJlYS5zZiAlPiUNCiAgc3RfdW5pb24oKQ0KDQojIGJvdW5kaW5nIGJveCBjb29yZGluYXRlcyBvZiBmb2N1cyBhcmVhDQptdW5pY2gkYXJlYS5iYm94IDwtIG11bmljaCRhcmVhLnVuaW9uICU+JQ0KICBzdF9iYm94KGNycyA9IHNmOjpzdF9jcnMoMzAzNSkpDQoNCiMgc3BlY2lmeSByYXN0ZXIgb2JqZWN0IGFuZCB0aWxlIGlkIG51bWJlcg0KbXVuaWNoJGFyZWEucmFzdGVyIDwtIG11bmljaC5yYXN0ZXIgJT4lDQogIHJhc3RlciguLCBsYXllciA9ICJ0aWxlLmlkIikNCg0KIyBzcGVjaWZ5IHJhc3RlciBvYmplY3QgYW5kIGVsZXZhdGlvbiB2YWx1ZSBvZiBlYWNoIHRpbGUgKGhlcmUgY29uc2lkZXJlZCBhcyBjb25zdGFudCkNCm11bmljaCRhcmVhLmVsZXZhdGlvbiA8LSBtdW5pY2gucmFzdGVyICU+JQ0KICByYXN0ZXIoLiwgbGF5ZXIgPSAiZWxldmF0aW9uIikNCg0KIyBudW1iZXIgb2YgdGlsZXMNCm11bmljaCRhcmVhLnBhcmFtc1tbInRpbGUubnVtIl1dIDwtIGxlbmd0aChtdW5pY2gkYXJlYS5kZiR0aWxlLmlkKQ0KDQojIHNpemUgb2YgdGlsZXMNCm11bmljaCRhcmVhLnBhcmFtc1tbImJhc2UudGlsZS5zaXplIl1dIDwtIGFzLm51bWVyaWMoc3FydChzdF9hcmVhKG11bmljaCRhcmVhLnNmWzEsXSkpKQ0KDQojIHN0b3JpbmcgZXZlcnl0aGluZyBpbiBhcmVhIG9iamVjdA0KYXJlYSA8LSBtdW5pY2gNCg0Kc2F2ZVJEUyhhcmVhLCAiRGF0YS9hcmVhLnJkcyIpDQpgYGANCg0KSW4gdGhlIGZvbGxvd2luZywgd2UgY2FuIHNlZSB0aGUgc3BhdGlhbCBkZW5zaXR5IG9mIHRoZSBHVFAgKHNlZSBGaWd1cmUgNCBpbiB0aGUgcGFwZXIpLg0KDQpgYGB7ciBndHAtbWFwLCBmaWcuY2FwPSJTcGF0aWFsIGRlbnNpdHkgb2YgdGhlIGdyb3VuZCB0cnV0aCBwb3B1bGF0aW9uIn0NCiMgYWRqdXN0YWJsZSBicmVhayBwb2ludHMgZm9yIG1hcCBjYXRlZ29yaWVzDQpicmVha3MgPC0gYygwLCAyLCA1LCAxMCwgMjAsIDUwLCAxMDAsIDIwMCwgMzUwLCBJbmYpDQojIHBsb3QgbWFwIGFuZCBwcmludA0KKEdUUC5zcGF0LmRlbnMucGxvdCA8LSBhcmVhJGFyZWEuc2YgJT4lIA0KICBtdXRhdGUocG9wLmNhdCA9IGN1dChwb3AsIGJyZWFrcyA9IGJyZWFrcywgZGlnLmxhYiA9IDcsIHJpZ2h0ID0gRikpICU+JSANCiAgbWFwX2RlbnNpdHkoZGF0YSA9IC4sIHZhciA9ICJwb3AuY2F0IiwgbGFiZWwgPSAiR1RQIikpDQojIGdnc2F2ZSgiRmluYWwgUGxvdHMvR1RQLnBuZyIsIEdUUC5zcGF0LmRlbnMucGxvdCwgZGV2aWNlID0gInBuZyIsIGJnID0gInRyYW5zcGFyZW50IikNCg0KYGBgDQoNCkluIHRoZSBmb2xsb3dpbmcsIHdlIGNhbiBzZWUgdGhlIGVtcGlyaWNhbCBjdW11bGF0aXZlIGNvbXBsZW1lbnRhcnkgZGlzdHJpYnV0aW9uIChFQ0NERikgb2YgdGhlIEdUUCAoc2VlIEZpZ3VyZSA0IGluIHRoZSBwYXBlcikuIFRoZSBjYXBwZWQgZW1waXJpY2FsIGN1bXVsYXRpdmUgZGlzdHJpYnV0aW9uIChFQ0RGKSBpcyBhZGRlZCBhcyBhbiBpbnNlcnQuDQoNCmBgYHtyIGRlbnNpdHktcGxvdCwgZmlnLmNhcD0iRUNDREYgYW5kIEVDREYgb2YgdGhlIGdyb3VuZCB0cnV0aCBwb3B1bGF0aW9uLCAoSW5zZXJ0IEVDREYgY2FwcGVkIGF0IDMwKSIsIHdhcm5pbmc9RkFMU0V9DQojIEVDQ0RGIGFuZCBFQ0RGIG9mIEdUUA0KKEdUUC5zcGF0LmRlbnMucGxvdCA8LSBkZW5zaXR5X3Bsb3RzKGFyZWEkYXJlYS5kZikpDQojIGdnc2F2ZSgiRmluYWwgUGxvdHMvRUNDREYucG5nIiwgR1RQLnNwYXQuZGVucy5wbG90LCBkZXZpY2UgPSAicG5nIikNCg0KIyB3YXJuaW5nIGNhbiBiZSBpZ25vcmVkDQoNCmBgYA0KDQojIyBSYWRpbyBuZXR3b3JrDQoNClRoaXMgc3Vic2VjdGlvbiByZWZlcnMgdG8gdGhlIGRldmVsb3BtZW50IG9mIGEgcmFkaW8gbmV0d29yayB3aXRoaW4gb3VyIG9wZXJhdGluZyBhcmVhLiBEZXZlbG9waW5nIHRoZSByYWRpbyBuZXR3b3JrIGlzIGhlYXZpbHkgZGVwZW5kZW50IG9uIHRoZSBgbW9ibG9jYCBwYWNrYWdlLCB3aGljaCBpcyBwcm9tb3RlZCB0aHJvdWdoIHRoZSBFdXJvcGVhbiBTdGF0aXN0aWNhbCBTeXN0ZW0uIEhvd2V2ZXIsIHdlIGhhdmUgYWRqdXN0ZWQgYW5kIGV4dGVuZGVkIHNvbWUgb2YgdGhlIGZ1bmN0aW9ucyB0byBvdXIgbmVlZHMsIGUuZy4gY29uY2VybmluZyB0aGUgZ2VuZXJhdGlvbiBvZiBtdWx0aXBsZSBjZWxsIGxheWVycy4NCg0KSW4gZ2VuZXJhbCwgdGhlIGBtb2Jsb2NgIHBhY2thZ2UgYWxsb3dzIHRvIGRlZmluZSBtYW55IHBhcmFtZXRlcnMsIGhvd2V2ZXIsIGlmIHRoZXkgYXJlIG5vdCBkZWZpbmVkLCBkZWZhdWx0IHBhcmFtZXRlcnMsIHNldCBieSB0aGUgcGFja2FnZSwgYXJlIHVzZWQuIFRoaXMgbWFrZXMgaXQgdmVyeSBlYXN5IHRvIGltcGxlbWVudCBhcyBtdWNoIC8gYXMgbGl0dGxlIGluZm9ybWF0aW9uIG9uZSBoYXMgb24gYSBjZXJ0YWluIG5ldHdvcmsgYW5kIGFsd2F5cyBnZXR0aW5nIGl0IHRvIHdvcmsuIFRoZSBmb2xsb3dpbmcgYWRqdXN0YWJsZSBwYXJhbWV0ZXJzIGFuZCBkZWZhdWx0IHZhbHVlcyBhcmUgcHJvdmlkZWQgdGhyb3VnaCB0aGUgcGFja2FnZSBmb3IgY3JlYXRpbmcgYSByYWRpbyBuZXR3b3JrIGFuZCBtb2RlbGxpbmcgdGhlIHNpZ25hbCBzdHJlbmd0aCBpbiBhIGZvY3VzIGFyZWE6DQoNCmBgYHtyfQ0KIyBwb3NzaWJsZSBtb2Jsb2MgcGFyYW1ldGVycw0KbW9ibG9jX3BhcmFtKCkNCmBgYA0KDQpGb3Igb3VyIHN0dWR5IHdlIGNvbnN0cnVjdCBmb3VyIG5ldHdvcmtzLiBUaGUgZmlyc3QgdHdvIG5ldHdvcmtzIGFyZSBvbmUtbGF5ZXIgbmV0d29ya3MgYW5kIGRpZmZlciBpbiBjZWxsIGRlbnNpdHksIHdoaWxlIHRoZSBvdGhlciB0d28gbmV0d29ya3MgYXJlIG11bHRpLWxheWVyIG5ldHdvcmtzLiBXZSBpbml0aWFsaXplIGVhY2ggbmV0d29yayB3aXRoIHRoZSBudW1iZXIgb2YgbGF5ZXJzIGFuZCBlYWNoIGxheWVyIHJlZmVycyB0byBhIGNlcnRhaW4ga2luZCBvZiBjZWxscyAoTWFjcm8sIE1lc28gYW5kIE1pY3JvKS4gVGhlIGRldmVsb3BtZW50IG9mIGVhY2ggbGF5ZXIgc3RhcnRzIHdpdGggYSBoZXhhZ29uYWwgZ3JpZCBpbiB3aGljaCB0aGUgcG9pbnRzIGRlZmluZSB0b3dlciBsb2NhdGlvbnMuIFRoZSBoZXhhZ29ucyBoYXZlIGRpZmZlcmVudCBzaXplcyAoaS5lLiB0b3dlciBkaXN0YW5jZSkgZGVwZW5kZW50IG9uIHRoZSBsYXllciBhbmQgbWF5IGNvbnRhaW4gc29tZSByYW5kb21uZXNzIHdpdGhpbiB0aGUgbGF5ZXIgKGppdHRlcikuIEVhY2ggbGF5ZXIgc3BhbnMgb3ZlciB0aGUgY29tcGxldGUgZm9jdXMgYXJlYS4NCg0KT24gZWFjaCB0b3dlciB0aHJlZSBkaXJlY3Rpb25hbCBjZWxscyBhcmUgcGxhY2VkIHRoYXQgYXJlIGRpcmVjdGVkIGluIGEgMTIwwrAgYW5nbGUgdG8gZWFjaCBvdGhlci4gQWxsIGxheWVycyBjb250YWluIGEgcm90YXRpb24gcGFyYW1ldGVyIChhemltdXRoKSB0byBwcmV2ZW50IGNlbGxzIG9mIGRpZmZlcmVudCBsYXllcnMgYnJvYWRjYXN0aW5nIGludG8gdGhlIGV4YWN0IHNhbWUgZGlyZWN0aW9uLCBpbiByZWZlcmVuY2UgdG8gdGhlIG9wZXJhdGluZyBhcmVhLiBJbiB0aGlzIHN0dWR5LCB3ZSBkbyAqbm90KiBpbXBsZW1lbnQgb21uaS1kaXJlY3Rpb25hbCBjZWxscywgdGhlcmVmb3JlLCBhbGwgYG1vYmxvY2AgcGFyYW1ldGVycyB3aXRoIHRoZSBzdWZmaXggIlxfc21hbGwiIGFyZSBub3QgdXNlZC4NCg0KVGhlIGNlbGxzIGFyZSBzcGVjaWZpZWQgd2l0aCBsYXllciBzcGVjaWZpYyBwYXJhbWV0ZXJzIChlLmcuIGhlaWdodCwgcG93ZXIsIHBhdGggbG9zcyBleHBvbmVudCwgZXRjLikuIEFsbCBjZWxscyBvZiBvbmUgbmV0d29yayBzY2VuYXJpbyBhcmUgc3BlY2lmaWVkIGluIGEgc28tY2FsbGVkIGNlbGxwbGFuLiBUaGlzIGNlbGxwbGFuIGlzIGEgZGF0YSBmcmFtZSBvbiB0aGUgY2VsbCBsZXZlbCwgd2hpY2ggY29udGFpbnMgYWxsIGluZm9ybWF0aW9uIG9uIHRoZSBwYXJhbWV0ZXJzLiBXaGVuIHRoZSBjZWxscGxhbiBpcyBjb21wbGV0ZWQgdGhlIGNlbGwtc3BlY2lmaWMgYnJvYWRjYXN0aW5nIHByb2ZpbGUgKGkuZS4gY2VsbCBwcm9maWxlKSBpcyBlc3RpbWF0ZWQgYW5kIHByb2plY3RlZCBvbnRvIHRoZSBvcGVyYXRpbmcgYXJlYS4gVGhlIGZ1bmN0aW9uIGBjb21wdXRlX3NpZ19zdHJlbmd0aCgpYCBjb21wdXRlcyB0aGUgZGlzdGFuY2UsIHNpZ25hbCBzdHJlbmd0aCBhbmQgc2lnbmFsIGRvbWluYW5jZSBiZXR3ZWVuIGFueSB0aWxlIGFuZCBhbnkgY2VsbC4gRnVydGhlcm1vcmUsIGEgbWluaW11bSBwYXJhbWV0ZXIgaXMgaW1wbGVtZW50ZWQgdGhhdCBkZWZpbmVzIHRoZSBtaW5pbXVtIHNpZ25hbCBkb21pbmFuY2UgdmFsdWUgYW4gY2VsbC10aWxlIHJlbGF0aW9uc2hpcCBuZWVkcyB0byBoYXZlIGluIG9yZGVyIHRvIGJlIGNvbnNpZGVyZWQgImNvdmVyZWQiLiBGb3IgdGhlIGdlbmVyYXRpb24gb2YgYSBuZXR3b3JrIHNjZW5hcmlvLCB3ZSBhc3N1cmUgdGhhdCBhbGwgdGlsZXMgYXJlIHN1ZmZpY2llbnRseSBjb3ZlcmVkLCBpLmUuIGVhY2ggbW9iaWxlIHBob25lcyB3aXRoaW4gZWFjaCB0aWxlIGhhdmUgYXQgbGVhc3Qgb25lIGNlbGwgdGhhdCBjb3ZlcnMgdGhlbSB3aXRoIGEgc2lnbmFsIGRvbWluYW5jZSB2YWx1ZSBncmVhdGVyIHRoYW4gdGhlIG1pbmltdW0gdGhyZXNob2xkLCBpbiB0aGlzIGNhc2UgMC4wNS4NCg0KSW4gdGhlIGZvbGxvd2luZywgZWFjaCBjZWxscGxhbiB3aWxsIGJlIHByZXNlbnRlZCByZXNwZWN0aXZlbHkuIEZpcnN0LCB3ZSB3aWxsIHJlcG9ydCBzb21lIGltcG9ydGFudCB0aGVvcmV0aWNhbCBuZXR3b3JrIHBhcmFtZXRlcnMuIFRoZW4sIGRpZmZlcmVudCBjb3ZlcmFnZSBtYXBzIHdpbGwgYmUgdmlzdWFsaXplZC4gRmluYWxseSwgYSBoaXN0b2dyYW0gb2YNCg0KIyMjIENlbGxwbGFuLjE6IE9uZSBsYXllciwgc3BhcnNlIG5ldHdvcmsNCg0KYGBge3IsIGNlbGxwbGFuLjEuZ2VuLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFLCBmaWcuaGVpZ2h0PTgsIGZpZy53aWR0aD0xMCwgZmlnLmNhcD0iVGhlb3JldGljYWwgcmFkaW8gbmV0d29yayBwYXJhbWV0ZXJzIGZvciBlYWNoIGxheWVyIn0NCiMgc3BlY2lmeSBwYXJhbWV0ZXJzIG9mIGVhY2ggY2VsbA0KTUUuY2VsbC5wYXJhbS5tb2Jsb2MgPC0gbW9ibG9jX3BhcmFtKFcgPSA1MCwgcmFuZ2UgPSA4MDAwLCBwbGUgPSAzLjcsIGhlaWdodCA9IDEwLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG1pZHBvaW50ID0gLTg1LCBzdGVlcG5lc3MgPSAwLjMsIHNpZ19kX3RoID0gMC4wNSkNCg0KbGF5ZXIubGlzdCA8LSBsaXN0KE1FLmNlbGwucGFyYW0ubW9ibG9jKQ0KDQojIGNyZWF0ZSBkYXRhZnJhbWUgZm9yIHRoZW9yZXRpY2FsIHNpZ25hbCBzdHJlbmd0aCBkaXN0cmlidXRpb24NCnBhcmFtLmRmIDwtIG1hcF9kZnIobGF5ZXIubGlzdCwgcmJpbmQuZGF0YS5mcmFtZSkgJT4lIA0KICBtdXRhdGUoY2VsbC5raW5kID0gYygiTUUiKSwNCiAgICAgICAgIGxhYmVsID0gYygiTWVzbyIpKSAlPiUgDQogIGRwbHlyOjpzZWxlY3QoY2VsbC5raW5kLCBsYWJlbCwgZXZlcnl0aGluZygpLCBkb21pbmFuY2UudGggPSBzaWdfZF90aCkNCg0KIyByZWR1Y2VkIGRhdGEgZnJhbWUgb2YgdGhlb3JldGljYWwgc2lnbmFsIHN0cmVuZ3RoIGRpc3RyaWJ1dGlvbg0KcGFyYW0uZGYucmVkdWNlZCA8LSBwYXJhbS5kZiAlPiUgDQogIGRwbHlyOjpzZWxlY3QoY2VsbC5raW5kLCBkb21pbmFuY2UudGgpDQoNCiMgdGhlb3JldGljYWwgc2lnbmFsIHN0cmVuZ3RoIHBhcmFtZXRlciBwbG90cw0Kc2lnLnByYW0ucGxvdHMgPC0gc2lnX3BhcmFtX3Bsb3RzKHBhcmFtLmRmID0gcGFyYW0uZGYsIHJhbmdlLm1heCA9IDIwMDAwLCBiYXNlX3NpemUgPSAxMSkNCg0Kc2V0LnNlZWQoMTAwKQ0KDQpsYXllci5wYXJhbXMuZXh0IDwtIGxpc3QoDQogIE1FID0gbGlzdCh0b3dlci5kaXN0ID0gNDEwMCwNCiAgICAgICAgICAgIHJvdGF0aW9uLmRlZyA9IDM1LA0KICAgICAgICAgICAgaml0dGVyID0gMywNCiAgICAgICAgICAgIHN1YnNjcmlwdCA9ICJNRSIsDQogICAgICAgICAgICBzZWVkID0gNywNCiAgICAgICAgICAgIG1vYmxvYy5wYXJhbXMgPSBNRS5jZWxsLnBhcmFtLm1vYmxvYykNCiAgDQopDQoNCg0KY2VsbHBsYW4uMS5sYXllci4xIDwtIGNvbXBsZXRlX2NlbGxwbGFuX2dlbihhcmVhID0gYXJlYSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGF5ZXIucGFyYW1zLmV4dCA9IGxheWVyLnBhcmFtcy5leHQsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHBhcmFtLmRmID0gcGFyYW0uZGYpDQoNCg0KDQojIGhvdyBtYW55IHRpbGVzIGFyZSBub3Qgc3VmZmljaWVudGx5IGNvdmVyZWQgKENoZWNrKQ0KcGFzdGUwKCJOdW1iZXIgb2YgdGlsZXMgd2hpY2ggZG8gbm90IHJlYWNoIHRoZSBzaWduYWwgZG9taW5hbmNlIHRocmVzaG9sZCBvZjogIiAsIHN1bShjZWxscGxhbi4xLmxheWVyLjEkc2lnbmFsLmRvbSRtaXNzaW5nKSkNCg0KDQpgYGANCg0KSGVyZSB3ZSBjYW4gc2VlIHNvbWUgaW1wb3J0YW50IHRoZW9yZXRpY2FsIG5ldHdvcmsgcGFyYW1ldGVycy4NCg0KYGBge3IgY2VsbHBsYW4uMS50aGVvLnBhcmFtLCBtZXNzYWdlPUZBTFNFfQ0KKGNlbGxwbGFuLjEudGhlby5wYXJhbSA8LSBnZ3B1YnI6OmFzX2dncGxvdChzaWcucHJhbS5wbG90cyRmaW5hbCkpDQpgYGANCg0KSGVyZSB3ZSBjYW4gc2VlIHNvbWUgaW1wb3J0YW50IHNwYXRpYWwgbmV0d29yayBwYXJhbWV0ZXJzLg0KDQpgYGB7ciBjZWxscGxhbi4xLnNwYXQucGFyYW0sIG1lc3NhZ2U9RkFMU0V9DQpjZWxwbGFuLjEuc3BhdC5wYXJhbSA8LSBjZWxsLnNwYXQuZGlhZyhhcmVhID0gYXJlYSwgY2VsbHBsYW4gPSBjZWxscGxhbi4xLmxheWVyLjEsIGxhYmVsLm5hbWUgPSAiQ2VsbHBsYW4uMSIpDQoNCmNlbHBsYW4uMS5zcGF0LnBhcmFtJHRpbGUuY292ZXJhZ2UuaGlzdA0KY2VscGxhbi4xLnNwYXQucGFyYW0kY292ZXJhZ2UubWFwLmNvbXBsZXRlDQpgYGANCg0KIyMjIENlbGxwbGFuLjI6IE9uZSBsYXllciwgZGVuc2UgbmV0d29yaw0KDQpgYGB7ciwgY2VsbHBsYW4uMi5nZW4sIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9DQojIHNwZWNpZnkgcGFyYW1ldGVycyBvZiBlYWNoIGNlbGwNCk1FLmNlbGwucGFyYW0ubW9ibG9jIDwtIG1vYmxvY19wYXJhbShXID0gNTAsIHJhbmdlID0gODAwMCwgcGxlID0gMy43LCBoZWlnaHQgPSAxMCwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBtaWRwb2ludCA9IC04NSwgc3RlZXBuZXNzID0gMC4zLCBzaWdfZF90aCA9IDAuMDUpDQoNCmxheWVyLmxpc3QgPC0gbGlzdChNRS5jZWxsLnBhcmFtLm1vYmxvYykNCg0KIyBjcmVhdGUgZGF0YWZyYW1lIGZvciB0aGVvcmV0aWNhbCBzaWduYWwgc3RyZW5ndGggZGlzdHJpYnV0aW9uDQpwYXJhbS5kZiA8LSBtYXBfZGZyKGxheWVyLmxpc3QsIHJiaW5kLmRhdGEuZnJhbWUpICU+JSANCiAgbXV0YXRlKGNlbGwua2luZCA9IGMoIk1FIiksDQogICAgICAgICBsYWJlbCA9IGMoIk1lc28iKSkgJT4lIA0KICBkcGx5cjo6c2VsZWN0KGNlbGwua2luZCwgbGFiZWwsIGV2ZXJ5dGhpbmcoKSwgZG9taW5hbmNlLnRoID0gc2lnX2RfdGgpDQoNCiMgcmVkdWNlZCBkYXRhIGZyYW1lIG9mIHRoZW9yZXRpY2FsIHNpZ25hbCBzdHJlbmd0aCBkaXN0cmlidXRpb24NCnBhcmFtLmRmLnJlZHVjZWQgPC0gcGFyYW0uZGYgJT4lIA0KICBkcGx5cjo6c2VsZWN0KGNlbGwua2luZCwgZG9taW5hbmNlLnRoKQ0KDQojIHRoZW9yZXRpY2FsIHNpZ25hbCBzdHJlbmd0aCBwYXJhbWV0ZXIgcGxvdHMNCmNlbGxwbGFuLjIuc2lnLnByYW0ucGxvdHMgPC0gc2lnX3BhcmFtX3Bsb3RzKHBhcmFtLmRmID0gcGFyYW0uZGYsIHJhbmdlLm1heCA9IDIwMDAwLCBiYXNlX3NpemUgPSAxMSkNCg0Kc2V0LnNlZWQoMTAwKQ0KDQpsYXllci5wYXJhbXMuZXh0IDwtIGxpc3QoDQogIE1FID0gbGlzdCh0b3dlci5kaXN0ID0gMzAwMCwNCiAgICAgICAgICAgIHJvdGF0aW9uLmRlZyA9IDM1LA0KICAgICAgICAgICAgaml0dGVyID0gMSwNCiAgICAgICAgICAgIHN1YnNjcmlwdCA9ICJNRSIsDQogICAgICAgICAgICBzZWVkID0gNywNCiAgICAgICAgICAgIG1vYmxvYy5wYXJhbXMgPSBNRS5jZWxsLnBhcmFtLm1vYmxvYykNCiAgDQopDQoNCg0KY2VsbHBsYW4uMS5sYXllci4yIDwtIGNvbXBsZXRlX2NlbGxwbGFuX2dlbihhcmVhID0gYXJlYSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGF5ZXIucGFyYW1zLmV4dCA9IGxheWVyLnBhcmFtcy5leHQsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHBhcmFtLmRmID0gcGFyYW0uZGYpDQoNCg0KDQojIGhvdyBtYW55IHRpbGVzIGFyZSBub3Qgc3VmZmljaWVudGx5IGNvdmVyZWQNCnBhc3RlMCgiTnVtYmVyIG9mIHRpbGVzIHdoaWNoIGRvIG5vdCByZWFjaCB0aGUgc2lnbmFsIGRvbWluYW5jZSB0aHJlc2hvbGQgb2Y6ICIgLCBzdW0oY2VsbHBsYW4uMS5sYXllci4yJHNpZ25hbC5kb20kbWlzc2luZykpDQoNCmBgYA0KDQpIZXJlIHdlIGNhbiBzZWUgc29tZSBpbXBvcnRhbnQgdGhlb3JldGljYWwgbmV0d29yayBwYXJhbWV0ZXJzLg0KDQpgYGB7ciBjZWxscGxhbi4yLnRoZW8ucGFyYW0sIG1lc3NhZ2U9RkFMU0V9DQooY2VsbHBsYW4uMi50aGVvLnBhcmFtIDwtIGdncHVicjo6YXNfZ2dwbG90KGNlbGxwbGFuLjIuc2lnLnByYW0ucGxvdHMkZmluYWwpKQ0KYGBgDQoNCkhlcmUgd2UgY2FuIHNlZSBzb21lIGltcG9ydGFudCBzcGF0aWFsIG5ldHdvcmsgcGFyYW1ldGVycy4NCg0KYGBge3IgY2VsbHBsYW4uMi5zcGF0LnBhcmFtLCBtZXNzYWdlPUZBTFNFfQ0KY2VscGxhbi4yLnNwYXQucGFyYW0gPC0gY2VsbC5zcGF0LmRpYWcoYXJlYSA9IGFyZWEsIGNlbGxwbGFuID0gY2VsbHBsYW4uMS5sYXllci4yLCBsYWJlbC5uYW1lID0gIkNlbGxwbGFuLjIiKQ0KDQpjZWxwbGFuLjIuc3BhdC5wYXJhbSR0aWxlLmNvdmVyYWdlLmhpc3QNCmNlbHBsYW4uMi5zcGF0LnBhcmFtJGNvdmVyYWdlLm1hcC5jb21wbGV0ZQ0KYGBgDQoNCiMjIyBDZWxscGxhbi4zOiBUaHJlZSBsYXllciBuZXR3b3JrDQoNCmBgYHtyLCBjZWxscGxhbi4zLmdlbiwgbWVzc2FnZT1GQUxTRX0NCiMgc3BlY2lmeSBwYXJhbWV0ZXJzIG9mIGVhY2ggY2VsbA0KTUEuY2VsbC5wYXJhbS5tb2Jsb2MgPC0gbW9ibG9jX3BhcmFtKFcgPSA1LCAjIFBvd2VyIGluIFdhdHRzDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcmFuZ2UgPSAxMDAwMCwgIyBtYXhpbXVtIGNvdmVyYWdlIHJhbmdlDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcGxlID0gMy40LCAjIFBhdGggbG9zcyBleHBvbmVudA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGhlaWdodCA9IDEwLCAjIGhlaWdodCBvZiB0aGUgYW50ZW5uYQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG1pZHBvaW50ID0gLTg1LCAjIG1pZHBvaW50IHBhcmFtZXRlciBvZiB0aGUgbG9naXN0aWMgZnVuY3Rpb24gZm9yIHNpZ25hbCBkb21pbmFuY2UNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzdGVlcG5lc3MgPSAwLjE1LCAjIHN0ZWVwbmVzcyBwYXJhbWV0ZXIgb2YgdGhlIGxvZ2lzdGljIGZ1bmN0aW9uIGZvciBzaWduYWwgZG9taW5hbmNlDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2lnX2RfdGggPSAwLjA1KSAjIGRvbWluYW5jZSBtaW5pbXVtIHRocmVzaG9sZCANCg0KTUUuY2VsbC5wYXJhbS5tb2Jsb2MgPC0gbW9ibG9jX3BhcmFtKFcgPSA1MCwgcmFuZ2UgPSAzNTAwLCBwbGUgPSAzLjgsIGhlaWdodCA9IDEwLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG1pZHBvaW50ID0gLTg1LCBzdGVlcG5lc3MgPSAwLjMsIHNpZ19kX3RoID0gMC4wNSkNCg0KTUkuY2VsbC5wYXJhbS5tb2Jsb2MgPC0gbW9ibG9jX3BhcmFtKFcgPSAxLCByYW5nZSA9IDM1MDAsIHBsZSA9IDQsIGhlaWdodCA9IDYsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbWlkcG9pbnQgPSAtODUsIHN0ZWVwbmVzcyA9IDAuNCwgc2lnX2RfdGggPSAwLjA1KQ0KDQoNCmxheWVyLmxpc3QgPC0gbGlzdChNQS5jZWxsLnBhcmFtLm1vYmxvYywgTUUuY2VsbC5wYXJhbS5tb2Jsb2MsIE1JLmNlbGwucGFyYW0ubW9ibG9jKQ0KDQojIGNyZWF0ZSBkYXRhZnJhbWUgZm9yIHRoZW9yZXRpY2FsIHNpZ25hbCBzdHJlbmd0aCBkaXN0cmlidXRpb24NCnBhcmFtLmRmIDwtIG1hcF9kZnIobGF5ZXIubGlzdCwgcmJpbmQuZGF0YS5mcmFtZSkgJT4lIA0KICBtdXRhdGUoY2VsbC5raW5kID0gYygiTUEiLCAiTUUiLCAiTUkiKSwNCiAgICAgICAgIGxhYmVsID0gYygiTWFjcm8iLCAiTWVzbyIsICJNaWNybyIpKSAlPiUgDQogIGRwbHlyOjpzZWxlY3QoY2VsbC5raW5kLCBsYWJlbCwgZXZlcnl0aGluZygpLCBkb21pbmFuY2UudGggPSBzaWdfZF90aCkNCg0KIyByZWR1Y2VkIGRhdGEgZnJhbWUgb2YgdGhlb3JldGljYWwgc2lnbmFsIHN0cmVuZ3RoIGRpc3RyaWJ1dGlvbg0KcGFyYW0uZGYucmVkdWNlZCA8LSBwYXJhbS5kZiAlPiUgDQogIGRwbHlyOjpzZWxlY3QoY2VsbC5raW5kLCBkb21pbmFuY2UudGgpDQoNCiMgdGhlb3JldGljYWwgc2lnbmFsIHN0cmVuZ3RoIHBhcmFtZXRlciBwbG90cw0KY2VsbHBsYW4uMy5zaWcucHJhbS5wbG90cyA8LSBzaWdfcGFyYW1fcGxvdHMocGFyYW0uZGYgPSBwYXJhbS5kZiwgcmFuZ2UubWF4ID0gMjAwMDAsIGJhc2Vfc2l6ZSA9IDExKQ0KDQpzZXQuc2VlZCgxMDApDQoNCmxheWVyLnBhcmFtcy5leHQgPC0gbGlzdCgNCiAgTUEgPSBsaXN0KHRvd2VyLmRpc3QgPSA4NTAwLA0KICAgICAgICAgICAgcm90YXRpb24uZGVnID0gMCwNCiAgICAgICAgICAgIGppdHRlciA9IDEwMDAsDQogICAgICAgICAgICBzdWJzY3JpcHQgPSAiTUEiLA0KICAgICAgICAgICAgc2VlZCA9IDMsDQogICAgICAgICAgICBtb2Jsb2MucGFyYW1zID0gTUEuY2VsbC5wYXJhbS5tb2Jsb2MpLA0KICBNRSA9IGxpc3QodG93ZXIuZGlzdCA9IDM1MDAsDQogICAgICAgICAgICByb3RhdGlvbi5kZWcgPSAzNSwNCiAgICAgICAgICAgIGppdHRlciA9IDcwMCwNCiAgICAgICAgICAgIHN1YnNjcmlwdCA9ICJNRSIsDQogICAgICAgICAgICBzZWVkID0gNywNCiAgICAgICAgICAgIG1vYmxvYy5wYXJhbXMgPSBNRS5jZWxsLnBhcmFtLm1vYmxvYyksDQogIE1JID0gbGlzdCh0b3dlci5kaXN0ID0gMTAwMDAsDQogICAgICAgICAgICByb3RhdGlvbi5kZWcgPSA2MCwNCiAgICAgICAgICAgIGppdHRlciA9IDIwMDAsDQogICAgICAgICAgICBzdWJzY3JpcHQgPSAiTUkiLA0KICAgICAgICAgICAgc2VlZCA9IDEwLA0KICAgICAgICAgICAgbW9ibG9jLnBhcmFtcyA9IE1JLmNlbGwucGFyYW0ubW9ibG9jKQ0KICANCikNCg0KDQpjZWxscGxhbi4zLmxheWVyIDwtIGNvbXBsZXRlX2NlbGxwbGFuX2dlbihhcmVhID0gYXJlYSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxheWVyLnBhcmFtcy5leHQgPSBsYXllci5wYXJhbXMuZXh0LA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcGFyYW0uZGYgPSBwYXJhbS5kZikNCg0KDQoNCiMgaG93IG1hbnkgdGlsZXMgYXJlIG5vdCBzdWZmaWNpZW50bHkgY292ZXJlZA0KcGFzdGUwKCJOdW1iZXIgb2YgdGlsZXMgd2hpY2ggZG8gbm90IHJlYWNoIHRoZSBzaWduYWwgZG9taW5hbmNlIHRocmVzaG9sZCBvZjogIiAsIHN1bShjZWxscGxhbi4zLmxheWVyJHNpZ25hbC5kb20kbWlzc2luZykpDQoNCmBgYA0KDQpIZXJlIHdlIGNhbiBzZWUgc29tZSBpbXBvcnRhbnQgdGhlb3JldGljYWwgbmV0d29yayBwYXJhbWV0ZXJzLg0KDQpgYGB7ciBjZWxscGxhbi4zLnRoZW8ucGFyYW0sIG1lc3NhZ2U9RkFMU0V9DQooY2VsbHBsYW4uMy50aGVvLnBhcmFtIDwtIGdncHVicjo6YXNfZ2dwbG90KGNlbGxwbGFuLjMuc2lnLnByYW0ucGxvdHMkZmluYWwpKQ0KYGBgDQoNCkhlcmUgd2UgY2FuIHNlZSBzb21lIGltcG9ydGFudCBzcGF0aWFsIG5ldHdvcmsgcGFyYW1ldGVycy4NCg0KYGBge3IgY2VsbHBsYW4uMy5zcGF0LnBhcmFtLCBtZXNzYWdlPUZBTFNFfQ0KY2VscGxhbi4zLnNwYXQucGFyYW0gPC0gY2VsbC5zcGF0LmRpYWcoYXJlYSA9IGFyZWEsIGNlbGxwbGFuID0gY2VsbHBsYW4uMy5sYXllciwgbGFiZWwubmFtZSA9ICJDZWxscGxhbi4zIikNCg0KY2VscGxhbi4zLnNwYXQucGFyYW0kdGlsZS5jb3ZlcmFnZS5oaXN0DQpjZWxwbGFuLjMuc3BhdC5wYXJhbSRjb3ZlcmFnZS5tYXAuY29tcGxldGUNCmBgYA0KDQojIyMgQ2VsbHBsYW4uNDogVHdvIGxheWVyIG5ldHdvcmsNCg0KYGBge3IsIGNlbGxwbGFuLjQuZ2VuLCBtZXNzYWdlPUZBTFNFfQ0KIyBzcGVjaWZ5IHBhcmFtZXRlcnMgb2YgZWFjaCBjZWxsDQpNRS5jZWxsLnBhcmFtLm1vYmxvYyA8LSBtb2Jsb2NfcGFyYW0oVyA9IDUwLCByYW5nZSA9IDgwMDAsIHBsZSA9IDMuNywgaGVpZ2h0ID0gMTAsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbWlkcG9pbnQgPSAtODUsIHN0ZWVwbmVzcyA9IDAuMywgc2lnX2RfdGggPSAwLjA1KQ0KDQpNSS5jZWxsLnBhcmFtLm1vYmxvYyA8LSBtb2Jsb2NfcGFyYW0oVyA9IDEsIHJhbmdlID0gMzUwMCwgcGxlID0gNCwgaGVpZ2h0ID0gNiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBtaWRwb2ludCA9IC04NSwgc3RlZXBuZXNzID0gMC40LCBzaWdfZF90aCA9IDAuMDUpDQoNCmxheWVyLmxpc3QgPC0gbGlzdChNRS5jZWxsLnBhcmFtLm1vYmxvYywgTUkuY2VsbC5wYXJhbS5tb2Jsb2MpDQoNCiMgY3JlYXRlIGRhdGFmcmFtZSBmb3IgdGhlb3JldGljYWwgc2lnbmFsIHN0cmVuZ3RoIGRpc3RyaWJ1dGlvbg0KcGFyYW0uZGYgPC0gbWFwX2RmcihsYXllci5saXN0LCByYmluZC5kYXRhLmZyYW1lKSAlPiUgDQogIG11dGF0ZShjZWxsLmtpbmQgPSBjKCJNRSIsICJNSSIpLA0KICAgICAgICAgbGFiZWwgPSBjKCJNZXNvIiwgIk1pY3JvIikpICU+JSANCiAgZHBseXI6OnNlbGVjdChjZWxsLmtpbmQsIGxhYmVsLCBldmVyeXRoaW5nKCksIGRvbWluYW5jZS50aCA9IHNpZ19kX3RoKQ0KDQojIHJlZHVjZWQgZGF0YSBmcmFtZSBvZiB0aGVvcmV0aWNhbCBzaWduYWwgc3RyZW5ndGggZGlzdHJpYnV0aW9uDQpwYXJhbS5kZi5yZWR1Y2VkIDwtIHBhcmFtLmRmICU+JSANCiAgZHBseXI6OnNlbGVjdChjZWxsLmtpbmQsIGRvbWluYW5jZS50aCkNCg0KIyB0aGVvcmV0aWNhbCBzaWduYWwgc3RyZW5ndGggcGFyYW1ldGVyIHBsb3RzDQpjZWxscGxhbi40LnNpZy5wcmFtLnBsb3RzIDwtIHNpZ19wYXJhbV9wbG90cyhwYXJhbS5kZiA9IHBhcmFtLmRmLCByYW5nZS5tYXggPSAyMDAwMCwgYmFzZV9zaXplID0gMTEpDQoNCnNldC5zZWVkKDEwMCkNCg0KbGF5ZXIucGFyYW1zLmV4dCA8LSBsaXN0KA0KICAgIE1FID0gbGlzdCh0b3dlci5kaXN0ID0gNDAwMCwNCiAgICAgICAgICAgIHJvdGF0aW9uLmRlZyA9IDM1LA0KICAgICAgICAgICAgaml0dGVyID0gNzAwLA0KICAgICAgICAgICAgc3Vic2NyaXB0ID0gIk1FIiwNCiAgICAgICAgICAgIHNlZWQgPSA3LA0KICAgICAgICAgICAgbW9ibG9jLnBhcmFtcyA9IE1FLmNlbGwucGFyYW0ubW9ibG9jKSwNCiAgICBNSSA9IGxpc3QodG93ZXIuZGlzdCA9IDEwMDAwLA0KICAgICAgICAgICAgcm90YXRpb24uZGVnID0gNjAsDQogICAgICAgICAgICBqaXR0ZXIgPSAyMDAwLA0KICAgICAgICAgICAgc3Vic2NyaXB0ID0gIk1JIiwNCiAgICAgICAgICAgIHNlZWQgPSAxMCwNCiAgICAgICAgICAgIG1vYmxvYy5wYXJhbXMgPSBNSS5jZWxsLnBhcmFtLm1vYmxvYykNCiAgDQopDQoNCg0KY2VsbHBsYW4uMi5sYXllciA8LSBjb21wbGV0ZV9jZWxscGxhbl9nZW4oYXJlYSA9IGFyZWEsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsYXllci5wYXJhbXMuZXh0ID0gbGF5ZXIucGFyYW1zLmV4dCwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHBhcmFtLmRmID0gcGFyYW0uZGYpDQoNCg0KDQojIGhvdyBtYW55IHRpbGVzIGFyZSBub3Qgc3VmZmljaWVudGx5IGNvdmVyZWQNCnBhc3RlMCgiTnVtYmVyIG9mIHRpbGVzIHdoaWNoIGRvIG5vdCByZWFjaCB0aGUgc2lnbmFsIGRvbWluYW5jZSB0aHJlc2hvbGQgb2Y6ICIgLCBzdW0oY2VsbHBsYW4uMi5sYXllciRzaWduYWwuZG9tJG1pc3NpbmcpKQ0KDQpgYGANCg0KSGVyZSB3ZSBjYW4gc2VlIHNvbWUgaW1wb3J0YW50IHRoZW9yZXRpY2FsIG5ldHdvcmsgcGFyYW1ldGVycy4NCg0KYGBge3IgY2VsbHBsYW4uNC50aGVvLnBhcmFtLCBtZXNzYWdlPUZBTFNFfQ0KKGNlbGxwbGFuLjQudGhlby5wYXJhbSA8LSBnZ3B1YnI6OmFzX2dncGxvdChjZWxscGxhbi40LnNpZy5wcmFtLnBsb3RzJGZpbmFsKSkNCmBgYA0KDQpIZXJlIHdlIGNhbiBzZWUgc29tZSBpbXBvcnRhbnQgc3BhdGlhbCBuZXR3b3JrIHBhcmFtZXRlcnMuDQoNCmBgYHtyIGNlbGxwbGFuLjQuc3BhdC5wYXJhbSwgbWVzc2FnZT1GQUxTRX0NCmNlbHBsYW4uNC5zcGF0LnBhcmFtIDwtIGNlbGwuc3BhdC5kaWFnKGFyZWEgPSBhcmVhLCBjZWxscGxhbiA9IGNlbGxwbGFuLjIubGF5ZXIsIGxhYmVsLm5hbWUgPSAiQ2VsbHBsYW4uNCIpDQoNCmNlbHBsYW4uNC5zcGF0LnBhcmFtJHRpbGUuY292ZXJhZ2UuaGlzdA0KY2VscGxhbi40LnNwYXQucGFyYW0kY292ZXJhZ2UubWFwLmNvbXBsZXRlDQpgYGANCg0KQWZ0ZXIgdGhpcyBwcmVzZW50YXRpb24gb2YgZWFjaCBjZWxscGxhbiB3ZSB3aWxsIHBlcmZvcm0gbm93IHRoZSBwaG9uZS10by1jZWxsIGFzc29jaWF0aW9uLCB3aGljaCBpcyByZXNwb25zaWJsZSBmb3IgY3JlYXRpbmcgZWFjaCB2ZWN0b3IgJGMkLg0KDQojIyBEZXZpY2UtdG8tY2VsbCBhc3NvY2lhdGlvbiAoR2VuZXJhdGl2ZSBtb2RlbCkNCg0KSGVyZSB3ZSBicmluZyB0aGUgbW9iaWxlIHBob25lIHBvcHVsYXRpb24gd2l0aCBlYWNoIG5ldHdvcmsgdG9nZXRoZXIgYW5kIHN0b2NoYXN0aWNhbGx5IGFzc2lnbiB0aGUgbW9iaWxlIHBob25lIHBvcHVsYXRpb24gaW4gZWFjaCB0aWxlIHRvIHRoZSByZXNwZWN0aXZlIGNlbGxzLiBUaGUgcmVzdWx0IGlzIHRoZSBzby1jYWxsZWQgYy12ZWN0b3IgKGkuZS4sICRjJCksIHdoaWNoIGRlc2NyaWJlcyB0aGUgbnVtYmVyIG9mIG1vYmlsZSBwaG9uZXMgYXNzaWduZWQgdG8gZWFjaCBjZWxsLiBUaGUgYmFzaXMgcGFyYW1ldGVyIGZvciB0aGlzIGFzc2lnbm1lbnQgaXMgdGhlIHNpZ25hbCBkb21pbmFuY2Ugd2hpY2ggaXMgbm9ybWFsaXplZCBpbiB0aGUgZm9ybSBvZiBhIGNvbmRpdGlvbmFsIHByb2JhYmlsaXR5LiBUaGVzZSBjb25kaXRpb25hbCBwcm9iYWJpbGl0aWVzIChlbWlzc2lvbiBwcm9iYWJpbGl0aWVzKSBkZXNjcmliZSB0aGUgZWxlbWVudHMgb2YgJFAkLg0KDQpGaXJzdCwgd2UgY29tYmluZSBhbmQgc2F2ZSBhbGwgY2VsbHBsYW4gb2JqZWN0cyBpbnRvIHRoZSBsaXN0IG9iamVjdCBgY2VsbHBsYW5zLmxpc3RgLCB3aGljaCBoZWxwcyB3aXRoIGF1dG9tYXRpemluZyBlYWNoIHBob25lLXRvLWNlbGwgYXNzb2NpYXRpb24gdGFzay4NCg0KYGBge3J9DQpjZWxscGxhbnMubGlzdCA8LSBsaXN0KA0KICBjZWxscGxhbi4xID0gY2VsbHBsYW4uMS5sYXllci4xLA0KICBjZWxscGxhbi4yID0gY2VsbHBsYW4uMS5sYXllci4yLA0KICBjZWxscGxhbi4zID0gY2VsbHBsYW4uMy5sYXllciwNCiAgY2VsbHBsYW4uNCA9IGNlbGxwbGFuLjIubGF5ZXINCikNCg0KIyBzYXZlUkRTKGNlbGxwbGFucy5saXN0LCAiRGF0YS9jZWxscGxhbnMubGlzdC5yZHMiKQ0KYGBgDQoNClRoZW4sIHdlIHBlcmZvcm0gdGhlIHBob25lLXRvLWNlbGwgYXNzb2NpYXRpb24uIEZpbmFsbHksIHdlIHNhdmUgdGhlIG9iamVjdCBgZ2VuLm1vZGVsLm9iamVjdHNgLCB3aGljaCB3aWxsIGNvbnRhaW4gdGhyZWUgb2JqZWN0cyBmb3IgZnVydGhlciB1c2UgaW4gdGhlIHdvcmtmbG93Og0KDQotICAgYEMudmVjLmRmLmxpc3RgOiBUaGlzIG9iamVjdCBjb250YWlucyB0aGUgYy12ZWN0b3IgZm9yIGVhY2ggbmV0d29yayBzY2VuYXJpby4NCg0KLSAgIGBQLmxvbmcuY29tcGxldGUuZGZgOiBUaGlzIG9iamVjdCBjb250YWlucyB0aGUgZm9yIGVhY2ggbmV0d29yayBzY2VuYXJpbyB0aGUgdGlsZS1jZWxsIHJlbGF0aW9uc2hpcHMgKGVkZ2UgbGlzdCksIHdoaWNoIGFyZSBjaGFyYWN0ZXJpemVkIGJ5IHRoZSB2YXJpYWJsZXMgZW1pc3Npb24gcHJvYmFiaWxpdGllcyAoZ2VuZXJhdGl2ZSBtb2RlbCksIHNpZ25hbCBzdHJlbmd0aCBhbmQgc2lnbmFsIGRvbWluYW5jZSB2YWx1ZXMuIE5vdGUsIHRoaXMgb2JqZWN0IGNvbnRhaW5zIGFsc28gdGhlICJ6ZXJvIiBlbGVtZW50cywgbWVhbmluZyB0aWxlLWNlbGwgcmVsYXRpb25zaGlwcyB0aGF0IGhhdmUgYW4gZW1pc3Npb24gcHJvYmFiaWxpdHkgb2YgMC4gQWxzbywgaXQgY29udGFpbnMgY2VydGFpbiBjZWxscGxhbiBwYXJhbWV0ZXJzLCBtYWtpbmcgaXQgYSB2ZXJ5IGxhcmdlIGRhdGFmcmFtZSBmb3IgZWFjaCBuZXR3b3JrIHNjZW5hcmlvLg0KDQotICAgYFAubG9uZy5kZmA6IFRoaXMgb2JqZWN0IGlzIGEgcmVkdWNlZCB2ZXJzaW9uIG9mIGBQLmxvbmcuY29tcGxldGUuZGZgLCB3aXRob3V0IHplcm8gZWxlbWVudHMgYW5kIG5vIGNlbGxwbGFuIHBhcmFtZXRlcnMuDQoNCmBgYHtyIGRldi10by1jZWxsfQ0KIyBzcGVjaWZ5IHRoZSBvcHRpb24gb2YgZGlmZmVyaW5nIHBhcmFtZXRlcnMgZm9yIHNpZ19kX3RoIGFuZCBtYXhfb3ZlcmxhcHBpbmdfY2VsbHMgZGVwZW5kaW5nIG9uIHRoZSBjZWxsIHR5cGUgaW4gY3VzdG9tIGNyZWF0ZV9zdHJlbmd0aF9sbGggZnVuY3Rpb24NCg0KIyBXb3JrYXJvdW5kOiBzZWN1cmluZyB0aGF0IHNpZ19kX3RoIGFuZCBtYXhfb3ZlcmxhcHBpbmdfY2VsbHMgYXJlIHRoZSBzYW1lIGZvciBlYWNoIGxheWVyDQpzaWduYWwuc3RyZW5ndGgubGxoLnBhcmFtIDwtIGxpc3Qoc2lnX2RfdGggPSAwLjA1LA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG1heF9vdmVybGFwcGluZ19jZWxscyA9IDEwMCkNCg0KIyBkZWZpbmUgY29ubmVjdGlvbiBsbGggYW5kIGNsYXNzaWZ5IHRpbGVzIGRlcGVuZGluZyBvbiBjb3ZlcmFnZSBzdGF0dXMNCmNvbm5lY3Rpb24ubGxoLmxpc3QgPC0gbWFwKGNlbGxwbGFucy5saXN0LCB+Y3JlYXRlX3N0cmVuZ3RoX2xsaF9jdXN0b20oc2lnbmFsLnN0cmVuZ3RoLmNvbWIuZHQgPSAueCRzaWduYWwuc3RyZW5ndGguY29tYi5kdCwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2lnbmFsLnN0cmVuZ3RoLmxsaC5wYXJhbSA9IHNpZ25hbC5zdHJlbmd0aC5sbGgucGFyYW0sDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNtYXJ0LnJvdW5kaW5nLmRpZ2l0cyA9IDMsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGFyZWEuZGYgPSBhcmVhJGFyZWEuZGYpKQ0KDQoNCiMgY3JlYXRlIGMtdmVjdG9yDQpDLnZlYy5kZi5saXN0IDwtIG1hcChjb25uZWN0aW9uLmxsaC5saXN0LCB+Y3JlYXRlX2NfdmVjdG9yKHNpZ25hbC5zdHJlbmd0aC5sbGguY29tYmluZWQgPSAueCRzaWduYWwuc3RyZW5ndGgubGxoLmNvbWJpbmVkKSkNCg0KIyBkZXZlbG9wIGxvbmcgZm9ybWF0IG9mIFAgbWF0cml4IHdoaWNoIGFsc28gY29udGFpbnMgY2VydGFpbiBjZWxscGxhbiBwYXJhbWV0ZXJzIGFuZCAiemVybyBlbGVtZW50cyINCiMgZGV2ZWxvcCBkaWZmZXJlbnQgdmVyc2lvbnMgb2YgaWQgdmFyaWFibGVzICh0aWxlIGFuZCBhbnRlbm5hcykgZm9yIGVhc2llciBqb2luaW5nDQpQLmxvbmcuY29tcGxldGUuZGYgPC0gbWFwMihjb25uZWN0aW9uLmxsaC5saXN0LCBDLnZlYy5kZi5saXN0LCB+ZnVsbF9qb2luKC54JHNpZ25hbC5zdHJlbmd0aC5sbGguY29tYmluZWQsIC55LCBieSA9ICJjZWxsIikpICU+JQ0KICBtYXAofmRwbHlyOjpzZWxlY3QoLiwgdGlsZS5pZC5jaHIsLCB0aWxlLmlkLmZhYywgdGlsZS5pZC5udW0sIHBvcCwgY2VsbCwgdHlwZSwgZGlzdCwgcGlqLCBwaG9uZXMuc3VtKSkgJT4lDQogIG1hcCh+bXV0YXRlKC4sIGNlbGwuY2hyID0gYXMuY2hhcmFjdGVyKGNlbGwpKSkgJT4lDQogIG1hcDIoY2VsbHBsYW5zLmxpc3QsIH5tdXRhdGUoLngsIGNlbGwuZmFjID0gZmFjdG9yKGNlbGwuY2hyLCBsZXZlbHMgPSBmY3RfdW5pcXVlKC55JGNlbGxwbGFuLmNvbWJpbmVkLmRmJGNlbGwuZmFjKSkpKSAlPiUNCiAgbWFwKH5tdXRhdGUoLiwgY2VsbC5udW0gPSBhcy5udW1lcmljKGNlbGwuZmFjKSkpDQoNCiMgTG9uZyBmb3JtYXQgb2YgUCBtYXRyaXggd2l0aCBtaW5pbWFsIHZhcmlhYmxlcyBhbmQgYW5kIHVuaXF1ZSByb3dzDQpQLmxvbmcuZGYgPC0gUC5sb25nLmNvbXBsZXRlLmRmICU+JQ0KICBtYXAofmRwbHlyOjpzZWxlY3QoLiwgdGlsZS5pZC5jaHIsIHRpbGUuaWQuZmFjLCB0aWxlLmlkLm51bSwgY2VsbC5jaHIsIGNlbGwuZmFjLCBjZWxsLm51bSwgcGlqKSkgJT4lDQogIG1hcCh+ZGlzdGluY3QoLikpDQoNCg0KZ2VuLm1vZGVsLm9iamVjdHMgPC0gbGlzdChDLnZlYy5kZi5saXN0ID0gQy52ZWMuZGYubGlzdCwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgUC5sb25nLmNvbXBsZXRlLmRmID0gUC5sb25nLmNvbXBsZXRlLmRmLA0KICAgICAgICAgICAgICAgICAgICAgICAgICBQLmxvbmcuZGYgPSBQLmxvbmcuZGYpDQoNCiMgc2F2ZVJEUyhnZW4ubW9kZWwub2JqZWN0cywgIkRhdGEvZ2VuLm1vZGVsLm9iamVjdHMucmRzIikNCg0KYGBgDQoNCmBgYHtyfQ0Kc2Vzc2lvbkluZm8oKQ0KYGBgDQo=