Date: 04/12/2022

To: The Head Biologist

From: Md Ashraful Islam Bhuiya

Subject: Identification of the putative site of the origin of replication in the CMV genome by computational analysis.

To suggest the putative regions of origin of replication in the CMV genome, we took the approach to detect tight clusters of complementary palindromes in the CMV genome. CMV genome is 229,354 base pair (bp) long and contains 296 palindromes that are at least 10 bp long. We considered the statistical model, the Homogeneous Poisson process (HPP), as the base model. HPP is a natural model for uniform random scatter. It assumes that events are non-overlapping, occur at a known constant rate, and are independent of each other. According to the HPP model, palindromes are scattered uniformly as well as randomly across the CMV genome. The number of palindromes in any location of the genome is independent of the number of palindromes in a non-overlapping, another location. If we observe the histogram in graph A below, where the number of palindromes in each 5000 bp is plotted, we find that a ~ similar number of palindromes are scattered all over the genome with some heterogeneity. For example, we can see only two locations where there are excess numbers of palindromes, 18 and 12, compared to other regions. Therefore, we assumed that the distribution of palindromes in the CMV genome broadly fits the HPP model, but with some heterogeneity. A small deviation from HPP (for example, a tight cluster of a number of palindromes) might give us our target locations. We analyzed the CMV genome to identify any locations with tight clusters of palindromes by considering the HPP as a baseline.

Finally, with this model, we have detected a number of tight clusters of palindromes at the locations from 87717 bp - 97488 bp, and 189810 bp - 198709 bp in the CMV genomes. The tightest clusters we have detected are from 90251 - 94174 bp. Anyone or a group of these clusters has significant potential to possess the origin of replication.

cmv<-read.csv("http://www.uwyo.edu/buerkle/compbio/statlabs/data/hcmv.data.txt")$location

par(mfrow=c(2,1), mar= c(5, 6, 3.5, 0))

 hist(cmv, breaks=seq(0,232000, by=5000), # creating a histogram, where each bar shows the counts of palindromes in every 5000 bp.
                 labels=TRUE, 
                 main = "Graph A: Distribution of palindromes in each 5000 bp in CMV genome", 
                 cex.main= 3, 
                 cex.axis=2.5, 
                 xlab = "Locations in the CMV genome", 
                 ylab = "Counts of the palindromes",
                 cex.lab=2.5
                 )

abline(h = qpois((0.95), lambda=5000*296/229354), col= "purple", lwd=3) #Add a horizontal line to the histogram that shows 95% quantile using the quantile function of the Poisson distribution qpois().




slide.window<-function(seqdata, total.length, blocksize=500, incr=100){  # creating a function for the sliding window, where block size can be specified and each block moves a given number of bp. 
  block.right<-seq(blocksize,total.length, by=incr)

  block.left<-block.right-blocksize+1

  block.mid<-(block.right+block.left)/2

  nblocks<-length(block.right)

  count<-numeric(nblocks)

  rate<-numeric(nblocks)

  

  for(i in 1:nblocks) {

    focal<-seqdata[seqdata>block.left[i] & seqdata < block.right[i]] # assigning the locations from seqdata (CMV data in our case) when locations are greater than left block and smaller than the right block. 

    count[i]<-length(focal) # total number of locations fall inside ith block. 

    rate[i]<-count[i] / blocksize

    # alternatively

    #count[i] <- sum(seqdata>block.left[i] & seqdata < block.right[i])

  }

  data.frame(count, rate, block.left, block.mid, block.right) # Creating a dataframe including the variables mentioned here. 

}

plot(rate ~ block.mid, data=slide.window(cmv, 230000, blocksize=5000, incr=250), # Plotting rate vs the value of calculated middle block, when block size is 5000 bp and each block move only 250 bp.   
     type="l", 
     col= "black",
     lwd = 2,
     main = "Graph B: Sliding window with 250 bp increment",
     cex.main= 3, 
     cex.axis=2.5, 
     xlab = "Locations in the CMV genome", 
     ylab = "Rates (count/blocksize)",
     cex.lab=2.5)

lines(rate ~ block.mid, data=slide.window(cmv, 230000, blocksize=9000, incr=250), # creating a line plot over the previous plot with different color when block size is 9000 bp and each block move only 250 bp.
      col="purple",
      lwd = 2)
      
lines(rate ~ block.mid, data=slide.window(cmv, 230000, blocksize=18000, incr=250), 
      col="orange",
      lwd = 2)

lines(rate ~ block.mid, data=slide.window(cmv, 230000, blocksize=36000, incr=250), 
      col="red",
      lwd=2)

In graph A, a total number of palindromes in every 5000 bp is plotted. We see that mostly 3 to 10 palindromes in each 5000 bp are spread all over the genome. Only two locations we found that contain 18 and 12 palindromes. The purple line in the graph shows 95% quantile. From this line, we understand that there is only a 5% chance that we get 12 or more palindromes in each 5000 bp. Graph B is a sliding window that shows the rate of palindromes in 5000 bp block when we move blocks 250 bp. From the sliding window, we find a similar number of counts as shown in graph A when the block size is 5000 bp. We also found that if we count palindromes every 2000 to 10000 bp or produce a sliding window with a 2000 to 10000 bp block size, the distribution of palindromes all over the genome is similar. The sliding window also shows that larger block sizes dilute the signal of palindrome count significantly as we can see from the purple, orange, and red lines with 9000bp, 18000 bp, and 36000 bp block sizes respectively in graph B (especially with 18000 and 36000 bp). Because increasing block size increases the probability of repeated count of the same palindromes in blocks each time. By comparing graph A and the sliding window (graph B), we showed that the count of the number of palindromes in each 5000 bp is accurate and robust as we move blocks only 250 bp. We did not lose any significant amount of palindrome from the counting due to the block size we considered. Therefore, the locations from graph A, where we find the cluster of 18 and 12 palindromes are our potential candidates for the sites of origin of replication, as we supposed that they might be more tightly clustered together compare to other regions.


par(mfrow=c(1,2), mar=c(5,5.5,5.5,4.5))
plot(cmv[1:(296-14)], diff(cmv, lag=14), # plotting the locations of palindromes in X- axis and difference between 1st and 15th palindromes of each consecutive/ overlapping set of 15 palindromes using lag value inside diff() function. 
     main = "Graph C: Locations and distances between two terminal palindromes
     of all sets of 15  palidromes",
     cex.main=2.5,
     cex.lab = 2.5,
     cex.axis = 2.5,
     xlab="Locations in the CMV genome", 
     ylab="Observed distance (bp)", 
     ylim=c(0,25000),
     lwd=3)
abline(h=qgamma(c(0.02, 0.0002),rate =296/229354, shape=14 ), # Add two horizontal lines on the plot for 2% and 0.02% quantile using the quantile function of the gamma distribution.
       col=c("purple", "blue"), 
       lwd=2)
plot(dgamma(0:25000, rate =296/229354, shape=14), 0:25000, # Density plot of the gamma distribution of the distances between two terminal palindromes of sets of 15 palindromes. 
     main = "Graph D: Density plot of the gamma distribution",
     cex.main=3,
     cex.lab = 2.5,
     cex.axis = 2.5,
     type="l", 
     ylab="", 
     xlab="Density of the gamma",
     lwd=3)
abline(h=qgamma( c(0.02, 0.0002),rate =296/229354, shape=14 ), 
       col=c("purple","blue"), 
       lwd=2)
mtext(c(0.02, 0.0002), # Add text to the right hand side of horizontal lines. 
      side=4, 
      at=qgamma(c(0.02, 0.0002),
      rate =296/229354, shape=14 ), 
      col=c("purple","blue"), 
      las=1, 
      cex=2)

In graph C we observe the distance (y-axis) between 1st and 15th palindromes of each set of 15 palindromes in different locations (x-axis) of the genome. We found that the differences between the two terminal palindromes of most of the sets are from ~6000 to ~ 17000 bp. There are only two locations where distances are ~6000 bp or less (below the purple line in graph C). I analyzed sets of the different numbers of palindromes (sets of 10 to 18 palindromes). Each time I found a similar pattern of distribution of variable distances between two terminal palindromes of each set. However, analysis with the set of 15 palindromes resulted in a decent number of locations where distances between two terminal palindromes are highly (probability <0.02) or extremely (probability <0.0002) improbable. Therefore, I used the set of 15 palindromes (lag=14) in this analysis. Graph D is the density plot of the gamma distribution. In both graphs, purple and blue lines are showing 2% and 0.02% quantile (extremely small distances) of the gamma distribution. This indicates that there is an extremely low probability (<0.02) of the occurrence of 15 palindromes in these small distances by chance. If we compare the locations of these two regions with graph A, we find that they fall inside the similar regions we detected in graph A with 95% quantile. These seemingly tight clusters (sets of 15) of palindromes might have some significant biological roles, for example, may possess the origin of replication.

## save indexes of improbably small distances
myprecious.indexes<-which(pgamma(diff(cmv, lag=14), rate =296/229354, shape=14) < 0.02) # Creating a vector that contain indexes of the locations of 1st palindromes of sets of 15 palindromes, of which, the probability of distances between 1st and 15th palindrome is less than 0.02 using the cumulative density of function, pgamma(), of the gamma distribution.   
mystrictprecious.indexes<-which(pgamma(diff(cmv, lag=14), rate =296/229354, shape=14) < 0.0002)

par(mar=c(5, 5, 2,2))
plot(cmv[1:(296-14)], 1:(296-14), type='n', # Creating an empty plot. 
     ylab="Sets of 15 palindromes", 
     xlab="Locations in the CMV genome", 
     main = "Graph E: Distances beween the two terminal palindromes of each set of 15 palindomes at different positions in CMV genome",
     cex.main= 2.2,
     cex.lab= 2.5,
     cex.axis = 2.5,
     lwd=3)
segments(cmv[1:(296-14)], 1:(296-14),  # Add distances between two terminal palindromes of each set of 15 palindromes as line segments.
         cmv[(1+14):296], 1:(296-14))
segments(cmv[myprecious.indexes], myprecious.indexes, # Add distances between two terminal palindromes of sets of palindromes indexed by myprecious as line segments with red color. This segments override the previous segments at the same locations. 
         cmv[myprecious.indexes+14], myprecious.indexes, col="red", lwd = 3) 

segments(cmv[mystrictprecious.indexes], mystrictprecious.indexes, 
         cmv[mystrictprecious.indexes+14], mystrictprecious.indexes, col= adjustcolor( "yellow", alpha.f = 0.7), lwd = 3)

To visualize distances as shown in graph C more clearly, we developed Graph E. It shows the distances between two terminal palindromes of sets of 15 palindromes by line segments. Here, each line indicates a set of 15 palindromes and the length of the line indicates the distance between the 1st and 15th palindromes of that set. From the x-axis, we can also understand the position of each set of palindromes in the CMV genome. We can visualize that there are two places in the genome (colored with both yellow and red) where lengths of sets of palindromes are significantly less (probability is less than 0.02) compared to the rest of the genome as also indicated in graph C. As I discussed before, with this extremely low probability (<0.02 (red+yellow regions) or <0.0002 (only yellow regions)) this is highly unlikely that these palindromes are in these positions of the genome just by chance. Based on these observations, primarily I made two recommendations below to start experimentally searching for the origin of replication.

Final recommendations

I would like to make two alternate recommendations. You may start with any one of these based on your overall goal, available time, and resources.

1. If you want to start analysis focusing on broader regions (with probability < 0.02), marked with red and yellow colors in the graph E.

library(kableExtra)

index_cmv1<- which(pgamma(diff(cmv, lag=14), rate =296/229354, shape=14) < 0.02) # Same as myprecious.indexes

broad_regions <- data.frame(Index=NA,  # Creating a dataframe called broad_regions.
                            Start=NA,
                            End=NA,
                            Distance=NA)

for (i in 1:length(index_cmv1)) {
  loc = index_cmv1[i]
  
  broad_regions[i,1] <- loc    # adding indexes to the first column of the dataframe. 
  broad_regions[i,2] <- cmv[loc] # adding starting locations of sets of 15 palindromes to the second column of the dataframe. 
  broad_regions[i,3] <- cmv[loc+14] # adding end locations of sets of 15 palindromes to the third column of the dataframe. 
  broad_regions[i,4] <- cmv[loc+14] - cmv[loc] # # adding distances between two terminals palindromes of sets 15 palindromes in the fourth column of the dataframe.  
  
}
tbl1<- kable(broad_regions, caption = "Table 1: locations of two terminal palindromes and distances between them of sets of 15 palindromes in the CMV genome (probability < 0.02)")
column_spec(tbl1, 1:4, width = "1in") # creating a table with a caption and defined width of columns. 
Table 1: locations of two terminal palindromes and distances between them of sets of 15 palindromes in the CMV genome (probability < 0.02)
Index Start End Distance
106 87717 92783 5066
107 88803 92859 4056
108 89586 93110 3524
109 90251 93250 2999
110 90763 93511 2748
111 91490 93601 2111
112 91637 94174 2537
113 91953 95975 4022
114 92526 97488 4962
243 189810 195262 5452
244 190918 195835 4917
248 192527 198195 5668
249 193447 198709 5262
NA

In the table above, each row shows the 1st (Start) and 15th (End) positions of a set of 15 palindromes and the distance (Distance) spanned by those palindromes. From the table, we find that the maximum distance a set of 15 palindromes span is ~5668 bp. The probability of these distances is < 0.02, which is highly improbable, which indicates that compared to other distances with a probability > 0.02, these distances are significantly smaller. So, we can say that 15 palindromes in a set are tightly packed together in these positions. And this is highly unlikely that they packed together by chance (with the probability of 0.02). This shows a clear deviation from the homogeneous Poisson process. Therefore, any one of these sets or a group of these sets of 15 palindromes may possess the site of the origin of replication. Based on the consecutive sets of palindromes (see Index in table 1), I would suggest starting the analysis focusing on these two regions, 87717 bp - 97488 bp, and 189810 bp - 198709 bp (table 1).

2. If you want to start the analysis by focusing on regions with extremely improbable distances (probability < 0.0002), marked with only yellow in the graph E.


index_cmv2<- which(pgamma(diff(cmv, lag=14), rate =296/229354, shape=14) < 0.0002) # same as above

smaller_regions <- data.frame(Index=NA,   #same as above
                            Start=NA,
                            End=NA,
                            Distance=NA)

for (i in 1:length(index_cmv2)) {
  loc = index_cmv2[i]
  
  smaller_regions[i,1] <- loc               #same as above.
  smaller_regions[i,2] <- cmv[loc]
  smaller_regions[i,3] <- cmv[loc+14]
  smaller_regions[i,4] <- cmv[loc+14] - cmv[loc]
}
tbl2 <- kable(smaller_regions, caption = "Table 2: locations of two terminal palindromes and distances between them of sets of 15 palindromes in the CMV genome (probability < 0.0002)")
column_spec(tbl2, 1:4, width = "1in")
Table 2: locations of two terminal palindromes and distances between them of sets of 15 palindromes in the CMV genome (probability < 0.0002)
Index Start End Distance
109 90251 93250 2999
110 90763 93511 2748
111 91490 93601 2111
112 91637 94174 2537
NA

If you want to start searching for the origin of replication within smaller regions, you can start with these regions as indicated in table 2. The exact locations of four sets of extremely packed 15 palindromes are given here. The maximum distance between two terminal palindromes of a set is 2999 bp and the minimum is only 2111 bp, which is extremely unlikely (with probability < 0.0002) that the palindromes in a set are very tightly clustered together just by chance. These extremely small distances also show a highly significant deviation from the Poisson process. Therefore, region from 90251 bp to 94174 bp has the highest probability to have the site of the origin of replication. So, it is worth starting to search with these regions for the origin of replication in the laboratory and if necessary, explore the other regions as mentioned in table 1.

LS0tDQpvdXRwdXQ6DQogIGh0bWxfbm90ZWJvb2s6DQogICAgY29kZV9mb2xkaW5nOiBoaWRlDQogIGh0bWxfZG9jdW1lbnQ6DQogICAgZGZfcHJpbnQ6IHBhZ2VkDQotLS0NCg0KDQojIyMjIERhdGU6IDA0LzEyLzIwMjINCg0KIyMjIyBUbzogICAgVGhlIEhlYWQgQmlvbG9naXN0IA0KDQojIyMjIEZyb206ICBNZCBBc2hyYWZ1bCBJc2xhbSBCaHVpeWENCg0KIyMjIF8qKipTdWJqZWN0OioqKiBJZGVudGlmaWNhdGlvbiBvZiB0aGUgcHV0YXRpdmUgc2l0ZSBvZiB0aGUgb3JpZ2luIG9mIHJlcGxpY2F0aW9uIGluIHRoZSBDTVYgZ2Vub21lIGJ5IGNvbXB1dGF0aW9uYWwgYW5hbHlzaXMuXw0KDQpUbyBzdWdnZXN0IHRoZSBwdXRhdGl2ZSByZWdpb25zIG9mIG9yaWdpbiBvZiByZXBsaWNhdGlvbiBpbiB0aGUgQ01WIGdlbm9tZSwgd2UgdG9vayB0aGUgYXBwcm9hY2ggdG8gZGV0ZWN0IHRpZ2h0IGNsdXN0ZXJzIG9mIGNvbXBsZW1lbnRhcnkgcGFsaW5kcm9tZXMgaW4gdGhlIENNViBnZW5vbWUuIENNViBnZW5vbWUgaXMgMjI5LDM1NCBiYXNlIHBhaXIgKGJwKSBsb25nIGFuZCBjb250YWlucyAyOTYgcGFsaW5kcm9tZXMgdGhhdCBhcmUgYXQgbGVhc3QgMTAgYnAgbG9uZy4gV2UgY29uc2lkZXJlZCB0aGUgc3RhdGlzdGljYWwgbW9kZWwsIHRoZSBIb21vZ2VuZW91cyBQb2lzc29uIHByb2Nlc3MgKEhQUCksIGFzIHRoZSBiYXNlIG1vZGVsLiAgSFBQIGlzIGEgbmF0dXJhbCBtb2RlbCBmb3IgdW5pZm9ybSByYW5kb20gc2NhdHRlci4gSXQgYXNzdW1lcyB0aGF0IGV2ZW50cyBhcmUgbm9uLW92ZXJsYXBwaW5nLCBvY2N1ciBhdCBhIGtub3duIGNvbnN0YW50IHJhdGUsIGFuZCBhcmUgaW5kZXBlbmRlbnQgb2YgZWFjaCBvdGhlci4gQWNjb3JkaW5nIHRvIHRoZSBIUFAgbW9kZWwsIHBhbGluZHJvbWVzIGFyZSBzY2F0dGVyZWQgdW5pZm9ybWx5IGFzIHdlbGwgYXMgcmFuZG9tbHkgYWNyb3NzIHRoZSBDTVYgZ2Vub21lLiBUaGUgbnVtYmVyIG9mIHBhbGluZHJvbWVzIGluIGFueSBsb2NhdGlvbiBvZiB0aGUgZ2Vub21lIGlzIGluZGVwZW5kZW50IG9mIHRoZSBudW1iZXIgb2YgcGFsaW5kcm9tZXMgaW4gYSBub24tb3ZlcmxhcHBpbmcsIGFub3RoZXIgbG9jYXRpb24uIElmIHdlIG9ic2VydmUgdGhlIGhpc3RvZ3JhbSBpbiBncmFwaCBBIGJlbG93LCB3aGVyZSB0aGUgbnVtYmVyIG9mIHBhbGluZHJvbWVzIGluIGVhY2ggNTAwMCBicCBpcyBwbG90dGVkLCB3ZSBmaW5kIHRoYXQgYSB+IHNpbWlsYXIgbnVtYmVyIG9mIHBhbGluZHJvbWVzIGFyZSBzY2F0dGVyZWQgYWxsIG92ZXIgdGhlIGdlbm9tZSB3aXRoIHNvbWUgaGV0ZXJvZ2VuZWl0eS4gRm9yIGV4YW1wbGUsIHdlIGNhbiBzZWUgb25seSB0d28gbG9jYXRpb25zIHdoZXJlIHRoZXJlIGFyZSBleGNlc3MgbnVtYmVycyBvZiBwYWxpbmRyb21lcywgMTggYW5kIDEyLCBjb21wYXJlZCB0byBvdGhlciByZWdpb25zLiBUaGVyZWZvcmUsIHdlIGFzc3VtZWQgdGhhdCB0aGUgZGlzdHJpYnV0aW9uIG9mIHBhbGluZHJvbWVzIGluIHRoZSBDTVYgZ2Vub21lIGJyb2FkbHkgZml0cyB0aGUgSFBQIG1vZGVsLCBidXQgd2l0aCBzb21lIGhldGVyb2dlbmVpdHkuIEEgc21hbGwgZGV2aWF0aW9uIGZyb20gSFBQIChmb3IgZXhhbXBsZSwgYSB0aWdodCBjbHVzdGVyIG9mIGEgbnVtYmVyIG9mIHBhbGluZHJvbWVzKSBtaWdodCBnaXZlIHVzIG91ciB0YXJnZXQgbG9jYXRpb25zLiBXZSBhbmFseXplZCB0aGUgQ01WIGdlbm9tZSB0byBpZGVudGlmeSBhbnkgbG9jYXRpb25zIHdpdGggdGlnaHQgY2x1c3RlcnMgb2YgcGFsaW5kcm9tZXMgYnkgY29uc2lkZXJpbmcgdGhlIEhQUCBhcyBhIGJhc2VsaW5lLiAgDQoNCkZpbmFsbHksIHdpdGggdGhpcyBtb2RlbCwgd2UgaGF2ZSBkZXRlY3RlZCBhIG51bWJlciBvZiB0aWdodCBjbHVzdGVycyBvZiBwYWxpbmRyb21lcyBhdCB0aGUgbG9jYXRpb25zIGZyb20gODc3MTcgYnAgLSA5NzQ4OCBicCwgYW5kIDE4OTgxMCBicCAtIDE5ODcwOSBicCBpbiB0aGUgQ01WIGdlbm9tZXMuIFRoZSB0aWdodGVzdCBjbHVzdGVycyB3ZSBoYXZlIGRldGVjdGVkIGFyZSBmcm9tIDkwMjUxIC0gOTQxNzQgYnAuIEFueW9uZSBvciBhIGdyb3VwIG9mIHRoZXNlIGNsdXN0ZXJzIGhhcyBzaWduaWZpY2FudCBwb3RlbnRpYWwgdG8gcG9zc2VzcyB0aGUgb3JpZ2luIG9mIHJlcGxpY2F0aW9uLiANCg0KYGBge3IsIGZpZy5oZWlnaHQ9NX0NCmNtdjwtcmVhZC5jc3YoImh0dHA6Ly93d3cudXd5by5lZHUvYnVlcmtsZS9jb21wYmlvL3N0YXRsYWJzL2RhdGEvaGNtdi5kYXRhLnR4dCIpJGxvY2F0aW9uDQoNCnBhcihtZnJvdz1jKDIsMSksIG1hcj0gYyg1LCA2LCAzLjUsIDApKQ0KDQogaGlzdChjbXYsIGJyZWFrcz1zZXEoMCwyMzIwMDAsIGJ5PTUwMDApLCAjIGNyZWF0aW5nIGEgaGlzdG9ncmFtLCB3aGVyZSBlYWNoIGJhciBzaG93cyB0aGUgY291bnRzIG9mIHBhbGluZHJvbWVzIGluIGV2ZXJ5IDUwMDAgYnAuDQogICAgICAgICAgICAgICAgIGxhYmVscz1UUlVFLCANCiAgICAgICAgICAgICAgICAgbWFpbiA9ICJHcmFwaCBBOiBEaXN0cmlidXRpb24gb2YgcGFsaW5kcm9tZXMgaW4gZWFjaCA1MDAwIGJwIGluIENNViBnZW5vbWUiLCANCiAgICAgICAgICAgICAgICAgY2V4Lm1haW49IDMsIA0KICAgICAgICAgICAgICAgICBjZXguYXhpcz0yLjUsIA0KICAgICAgICAgICAgICAgICB4bGFiID0gIkxvY2F0aW9ucyBpbiB0aGUgQ01WIGdlbm9tZSIsIA0KICAgICAgICAgICAgICAgICB5bGFiID0gIkNvdW50cyBvZiB0aGUgcGFsaW5kcm9tZXMiLA0KICAgICAgICAgICAgICAgICBjZXgubGFiPTIuNQ0KICAgICAgICAgICAgICAgICApDQoNCmFibGluZShoID0gcXBvaXMoKDAuOTUpLCBsYW1iZGE9NTAwMCoyOTYvMjI5MzU0KSwgY29sPSAicHVycGxlIiwgbHdkPTMpICNBZGQgYSBob3Jpem9udGFsIGxpbmUgdG8gdGhlIGhpc3RvZ3JhbSB0aGF0IHNob3dzIDk1JSBxdWFudGlsZSB1c2luZyB0aGUgcXVhbnRpbGUgZnVuY3Rpb24gb2YgdGhlIFBvaXNzb24gZGlzdHJpYnV0aW9uIHFwb2lzKCkuDQoNCg0KDQoNCnNsaWRlLndpbmRvdzwtZnVuY3Rpb24oc2VxZGF0YSwgdG90YWwubGVuZ3RoLCBibG9ja3NpemU9NTAwLCBpbmNyPTEwMCl7ICAjIGNyZWF0aW5nIGEgZnVuY3Rpb24gZm9yIHRoZSBzbGlkaW5nIHdpbmRvdywgd2hlcmUgYmxvY2sgc2l6ZSBjYW4gYmUgc3BlY2lmaWVkIGFuZCBlYWNoIGJsb2NrIG1vdmVzIGEgZ2l2ZW4gbnVtYmVyIG9mIGJwLiANCiAgYmxvY2sucmlnaHQ8LXNlcShibG9ja3NpemUsdG90YWwubGVuZ3RoLCBieT1pbmNyKQ0KDQogIGJsb2NrLmxlZnQ8LWJsb2NrLnJpZ2h0LWJsb2Nrc2l6ZSsxDQoNCiAgYmxvY2subWlkPC0oYmxvY2sucmlnaHQrYmxvY2subGVmdCkvMg0KDQogIG5ibG9ja3M8LWxlbmd0aChibG9jay5yaWdodCkNCg0KICBjb3VudDwtbnVtZXJpYyhuYmxvY2tzKQ0KDQogIHJhdGU8LW51bWVyaWMobmJsb2NrcykNCg0KICANCg0KICBmb3IoaSBpbiAxOm5ibG9ja3MpIHsNCg0KICAgIGZvY2FsPC1zZXFkYXRhW3NlcWRhdGE+YmxvY2subGVmdFtpXSAmIHNlcWRhdGEgPCBibG9jay5yaWdodFtpXV0gIyBhc3NpZ25pbmcgdGhlIGxvY2F0aW9ucyBmcm9tIHNlcWRhdGEgKENNViBkYXRhIGluIG91ciBjYXNlKSB3aGVuIGxvY2F0aW9ucyBhcmUgZ3JlYXRlciB0aGFuIGxlZnQgYmxvY2sgYW5kIHNtYWxsZXIgdGhhbiB0aGUgcmlnaHQgYmxvY2suIA0KDQogICAgY291bnRbaV08LWxlbmd0aChmb2NhbCkgIyB0b3RhbCBudW1iZXIgb2YgbG9jYXRpb25zIGZhbGwgaW5zaWRlIGl0aCBibG9jay4gDQoNCiAgICByYXRlW2ldPC1jb3VudFtpXSAvIGJsb2Nrc2l6ZQ0KDQogICAgIyBhbHRlcm5hdGl2ZWx5DQoNCiAgICAjY291bnRbaV0gPC0gc3VtKHNlcWRhdGE+YmxvY2subGVmdFtpXSAmIHNlcWRhdGEgPCBibG9jay5yaWdodFtpXSkNCg0KICB9DQoNCiAgZGF0YS5mcmFtZShjb3VudCwgcmF0ZSwgYmxvY2subGVmdCwgYmxvY2subWlkLCBibG9jay5yaWdodCkgIyBDcmVhdGluZyBhIGRhdGFmcmFtZSBpbmNsdWRpbmcgdGhlIHZhcmlhYmxlcyBtZW50aW9uZWQgaGVyZS4gDQoNCn0NCg0KcGxvdChyYXRlIH4gYmxvY2subWlkLCBkYXRhPXNsaWRlLndpbmRvdyhjbXYsIDIzMDAwMCwgYmxvY2tzaXplPTUwMDAsIGluY3I9MjUwKSwgIyBQbG90dGluZyByYXRlIHZzIHRoZSB2YWx1ZSBvZiBjYWxjdWxhdGVkIG1pZGRsZSBibG9jaywgd2hlbiBibG9jayBzaXplIGlzIDUwMDAgYnAgYW5kIGVhY2ggYmxvY2sgbW92ZSBvbmx5IDI1MCBicC4gICANCiAgICAgdHlwZT0ibCIsIA0KICAgICBjb2w9ICJibGFjayIsDQogICAgIGx3ZCA9IDIsDQogICAgIG1haW4gPSAiR3JhcGggQjogU2xpZGluZyB3aW5kb3cgd2l0aCAyNTAgYnAgaW5jcmVtZW50IiwNCiAgICAgY2V4Lm1haW49IDMsIA0KICAgICBjZXguYXhpcz0yLjUsIA0KICAgICB4bGFiID0gIkxvY2F0aW9ucyBpbiB0aGUgQ01WIGdlbm9tZSIsIA0KICAgICB5bGFiID0gIlJhdGVzIChjb3VudC9ibG9ja3NpemUpIiwNCiAgICAgY2V4LmxhYj0yLjUpDQoNCmxpbmVzKHJhdGUgfiBibG9jay5taWQsIGRhdGE9c2xpZGUud2luZG93KGNtdiwgMjMwMDAwLCBibG9ja3NpemU9OTAwMCwgaW5jcj0yNTApLCAjIGNyZWF0aW5nIGEgbGluZSBwbG90IG92ZXIgdGhlIHByZXZpb3VzIHBsb3Qgd2l0aCBkaWZmZXJlbnQgY29sb3Igd2hlbiBibG9jayBzaXplIGlzIDkwMDAgYnAgYW5kIGVhY2ggYmxvY2sgbW92ZSBvbmx5IDI1MCBicC4NCiAgICAgIGNvbD0icHVycGxlIiwNCiAgICAgIGx3ZCA9IDIpDQogICAgICANCmxpbmVzKHJhdGUgfiBibG9jay5taWQsIGRhdGE9c2xpZGUud2luZG93KGNtdiwgMjMwMDAwLCBibG9ja3NpemU9MTgwMDAsIGluY3I9MjUwKSwgDQogICAgICBjb2w9Im9yYW5nZSIsDQogICAgICBsd2QgPSAyKQ0KDQpsaW5lcyhyYXRlIH4gYmxvY2subWlkLCBkYXRhPXNsaWRlLndpbmRvdyhjbXYsIDIzMDAwMCwgYmxvY2tzaXplPTM2MDAwLCBpbmNyPTI1MCksIA0KICAgICAgY29sPSJyZWQiLA0KICAgICAgbHdkPTIpDQoNCmBgYA0KSW4gZ3JhcGggQSwgYSB0b3RhbCBudW1iZXIgb2YgcGFsaW5kcm9tZXMgaW4gZXZlcnkgNTAwMCBicCBpcyBwbG90dGVkLiBXZSBzZWUgdGhhdCBtb3N0bHkgMyB0byAxMCBwYWxpbmRyb21lcyBpbiBlYWNoIDUwMDAgYnAgYXJlIHNwcmVhZCBhbGwgb3ZlciB0aGUgZ2Vub21lLiBPbmx5IHR3byBsb2NhdGlvbnMgd2UgZm91bmQgdGhhdCBjb250YWluIDE4IGFuZCAxMiBwYWxpbmRyb21lcy4gVGhlIHB1cnBsZSBsaW5lIGluIHRoZSBncmFwaCBzaG93cyA5NSUgcXVhbnRpbGUuIEZyb20gdGhpcyBsaW5lLCB3ZSB1bmRlcnN0YW5kIHRoYXQgdGhlcmUgaXMgb25seSBhIDUlIGNoYW5jZSB0aGF0IHdlIGdldCAxMiBvciBtb3JlIHBhbGluZHJvbWVzIGluIGVhY2ggNTAwMCBicC4gR3JhcGggQiBpcyBhIHNsaWRpbmcgd2luZG93IHRoYXQgc2hvd3MgdGhlIHJhdGUgb2YgcGFsaW5kcm9tZXMgaW4gNTAwMCBicCBibG9jayB3aGVuIHdlIG1vdmUgYmxvY2tzIDI1MCBicC4gRnJvbSB0aGUgc2xpZGluZyB3aW5kb3csIHdlIGZpbmQgYSBzaW1pbGFyIG51bWJlciBvZiBjb3VudHMgYXMgc2hvd24gaW4gZ3JhcGggQSB3aGVuIHRoZSBibG9jayBzaXplIGlzIDUwMDAgYnAuIFdlIGFsc28gZm91bmQgdGhhdCBpZiB3ZSBjb3VudCBwYWxpbmRyb21lcyBldmVyeSAyMDAwIHRvIDEwMDAwIGJwIG9yIHByb2R1Y2UgYSBzbGlkaW5nIHdpbmRvdyB3aXRoIGEgMjAwMCB0byAxMDAwMCBicCBibG9jayBzaXplLCB0aGUgZGlzdHJpYnV0aW9uIG9mIHBhbGluZHJvbWVzIGFsbCBvdmVyIHRoZSBnZW5vbWUgaXMgc2ltaWxhci4gVGhlIHNsaWRpbmcgd2luZG93IGFsc28gc2hvd3MgdGhhdCBsYXJnZXIgYmxvY2sgc2l6ZXMgZGlsdXRlIHRoZSBzaWduYWwgb2YgcGFsaW5kcm9tZSBjb3VudCBzaWduaWZpY2FudGx5IGFzIHdlIGNhbiBzZWUgZnJvbSB0aGUgcHVycGxlLCBvcmFuZ2UsIGFuZCByZWQgbGluZXMgd2l0aCA5MDAwYnAsIDE4MDAwIGJwLCBhbmQgMzYwMDAgYnAgYmxvY2sgc2l6ZXMgcmVzcGVjdGl2ZWx5IGluIGdyYXBoIEIgKGVzcGVjaWFsbHkgd2l0aCAxODAwMCBhbmQgMzYwMDAgYnApLiBCZWNhdXNlIGluY3JlYXNpbmcgYmxvY2sgc2l6ZSBpbmNyZWFzZXMgdGhlIHByb2JhYmlsaXR5IG9mIHJlcGVhdGVkIGNvdW50IG9mIHRoZSBzYW1lIHBhbGluZHJvbWVzIGluIGJsb2NrcyBlYWNoIHRpbWUuIEJ5IGNvbXBhcmluZyBncmFwaCBBIGFuZCB0aGUgc2xpZGluZyB3aW5kb3cgKGdyYXBoIEIpLCB3ZSBzaG93ZWQgdGhhdCB0aGUgY291bnQgb2YgdGhlIG51bWJlciBvZiBwYWxpbmRyb21lcyBpbiBlYWNoIDUwMDAgYnAgaXMgYWNjdXJhdGUgYW5kIHJvYnVzdCBhcyB3ZSBtb3ZlIGJsb2NrcyBvbmx5IDI1MCBicC4gV2UgZGlkIG5vdCBsb3NlIGFueSBzaWduaWZpY2FudCBhbW91bnQgb2YgcGFsaW5kcm9tZSBmcm9tIHRoZSBjb3VudGluZyBkdWUgdG8gdGhlIGJsb2NrIHNpemUgd2UgY29uc2lkZXJlZC4gVGhlcmVmb3JlLCB0aGUgbG9jYXRpb25zIGZyb20gZ3JhcGggQSwgd2hlcmUgd2UgZmluZCB0aGUgY2x1c3RlciBvZiAxOCBhbmQgMTIgcGFsaW5kcm9tZXMgYXJlIG91ciBwb3RlbnRpYWwgY2FuZGlkYXRlcyBmb3IgdGhlIHNpdGVzIG9mIG9yaWdpbiBvZiByZXBsaWNhdGlvbiwgYXMgd2Ugc3VwcG9zZWQgdGhhdCB0aGV5IG1pZ2h0IGJlIG1vcmUgdGlnaHRseSBjbHVzdGVyZWQgdG9nZXRoZXIgY29tcGFyZSB0byBvdGhlciByZWdpb25zLiAgICAgICAgIA0KICAgICAgIA0KDQoNCmBgYHtyLCBmaWcuaGVpZ2h0PTUsIGZpZy53aWR0aD0xMCB9DQoNCnBhcihtZnJvdz1jKDEsMiksIG1hcj1jKDUsNS41LDUuNSw0LjUpKQ0KcGxvdChjbXZbMTooMjk2LTE0KV0sIGRpZmYoY212LCBsYWc9MTQpLCAjIHBsb3R0aW5nIHRoZSBsb2NhdGlvbnMgb2YgcGFsaW5kcm9tZXMgaW4gWC0gYXhpcyBhbmQgZGlmZmVyZW5jZSBiZXR3ZWVuIDFzdCBhbmQgMTV0aCBwYWxpbmRyb21lcyBvZiBlYWNoIGNvbnNlY3V0aXZlLyBvdmVybGFwcGluZyBzZXQgb2YgMTUgcGFsaW5kcm9tZXMgdXNpbmcgbGFnIHZhbHVlIGluc2lkZSBkaWZmKCkgZnVuY3Rpb24uIA0KICAgICBtYWluID0gIkdyYXBoIEM6IExvY2F0aW9ucyBhbmQgZGlzdGFuY2VzIGJldHdlZW4gdHdvIHRlcm1pbmFsIHBhbGluZHJvbWVzDQogICAgIG9mIGFsbCBzZXRzIG9mIDE1ICBwYWxpZHJvbWVzIiwNCiAgICAgY2V4Lm1haW49Mi41LA0KICAgICBjZXgubGFiID0gMi41LA0KICAgICBjZXguYXhpcyA9IDIuNSwNCiAgICAgeGxhYj0iTG9jYXRpb25zIGluIHRoZSBDTVYgZ2Vub21lIiwgDQogICAgIHlsYWI9Ik9ic2VydmVkIGRpc3RhbmNlIChicCkiLCANCiAgICAgeWxpbT1jKDAsMjUwMDApLA0KICAgICBsd2Q9MykNCmFibGluZShoPXFnYW1tYShjKDAuMDIsIDAuMDAwMikscmF0ZSA9Mjk2LzIyOTM1NCwgc2hhcGU9MTQgKSwgIyBBZGQgdHdvIGhvcml6b250YWwgbGluZXMgb24gdGhlIHBsb3QgZm9yIDIlIGFuZCAwLjAyJSBxdWFudGlsZSB1c2luZyB0aGUgcXVhbnRpbGUgZnVuY3Rpb24gb2YgdGhlIGdhbW1hIGRpc3RyaWJ1dGlvbi4NCiAgICAgICBjb2w9YygicHVycGxlIiwgImJsdWUiKSwgDQogICAgICAgbHdkPTIpDQpwbG90KGRnYW1tYSgwOjI1MDAwLCByYXRlID0yOTYvMjI5MzU0LCBzaGFwZT0xNCksIDA6MjUwMDAsICMgRGVuc2l0eSBwbG90IG9mIHRoZSBnYW1tYSBkaXN0cmlidXRpb24gb2YgdGhlIGRpc3RhbmNlcyBiZXR3ZWVuIHR3byB0ZXJtaW5hbCBwYWxpbmRyb21lcyBvZiBzZXRzIG9mIDE1IHBhbGluZHJvbWVzLiANCiAgICAgbWFpbiA9ICJHcmFwaCBEOiBEZW5zaXR5IHBsb3Qgb2YgdGhlIGdhbW1hIGRpc3RyaWJ1dGlvbiIsDQogICAgIGNleC5tYWluPTMsDQogICAgIGNleC5sYWIgPSAyLjUsDQogICAgIGNleC5heGlzID0gMi41LA0KICAgICB0eXBlPSJsIiwgDQogICAgIHlsYWI9IiIsIA0KICAgICB4bGFiPSJEZW5zaXR5IG9mIHRoZSBnYW1tYSIsDQogICAgIGx3ZD0zKQ0KYWJsaW5lKGg9cWdhbW1hKCBjKDAuMDIsIDAuMDAwMikscmF0ZSA9Mjk2LzIyOTM1NCwgc2hhcGU9MTQgKSwgDQogICAgICAgY29sPWMoInB1cnBsZSIsImJsdWUiKSwgDQogICAgICAgbHdkPTIpDQptdGV4dChjKDAuMDIsIDAuMDAwMiksICMgQWRkIHRleHQgdG8gdGhlIHJpZ2h0IGhhbmQgc2lkZSBvZiBob3Jpem9udGFsIGxpbmVzLiANCiAgICAgIHNpZGU9NCwgDQogICAgICBhdD1xZ2FtbWEoYygwLjAyLCAwLjAwMDIpLA0KICAgICAgcmF0ZSA9Mjk2LzIyOTM1NCwgc2hhcGU9MTQgKSwgDQogICAgICBjb2w9YygicHVycGxlIiwiYmx1ZSIpLCANCiAgICAgIGxhcz0xLCANCiAgICAgIGNleD0yKQ0KDQpgYGANCkluIGdyYXBoIEMgd2Ugb2JzZXJ2ZSB0aGUgZGlzdGFuY2UgKHktYXhpcykgYmV0d2VlbiAxc3QgYW5kIDE1dGggcGFsaW5kcm9tZXMgb2YgZWFjaCBzZXQgb2YgMTUgcGFsaW5kcm9tZXMgaW4gZGlmZmVyZW50IGxvY2F0aW9ucyAoeC1heGlzKSBvZiB0aGUgZ2Vub21lLiBXZSBmb3VuZCB0aGF0IHRoZSBkaWZmZXJlbmNlcyBiZXR3ZWVuIHRoZSB0d28gdGVybWluYWwgcGFsaW5kcm9tZXMgb2YgbW9zdCBvZiB0aGUgc2V0cyBhcmUgZnJvbSB+NjAwMCB0byB+IDE3MDAwIGJwLiBUaGVyZSBhcmUgb25seSB0d28gbG9jYXRpb25zIHdoZXJlIGRpc3RhbmNlcyBhcmUgfjYwMDAgYnAgb3IgbGVzcyAoYmVsb3cgdGhlIHB1cnBsZSBsaW5lIGluIGdyYXBoIEMpLiBJIGFuYWx5emVkIHNldHMgb2YgdGhlIGRpZmZlcmVudCBudW1iZXJzIG9mIHBhbGluZHJvbWVzIChzZXRzIG9mIDEwIHRvIDE4IHBhbGluZHJvbWVzKS4gRWFjaCB0aW1lIEkgZm91bmQgYSBzaW1pbGFyIHBhdHRlcm4gb2YgZGlzdHJpYnV0aW9uIG9mIHZhcmlhYmxlIGRpc3RhbmNlcyBiZXR3ZWVuIHR3byB0ZXJtaW5hbCBwYWxpbmRyb21lcyBvZiBlYWNoIHNldC4gSG93ZXZlciwgYW5hbHlzaXMgd2l0aCB0aGUgc2V0IG9mIDE1IHBhbGluZHJvbWVzIHJlc3VsdGVkIGluIGEgZGVjZW50IG51bWJlciBvZiBsb2NhdGlvbnMgd2hlcmUgZGlzdGFuY2VzIGJldHdlZW4gdHdvIHRlcm1pbmFsIHBhbGluZHJvbWVzIGFyZSBoaWdobHkgKHByb2JhYmlsaXR5IDwwLjAyKSBvciBleHRyZW1lbHkgKHByb2JhYmlsaXR5IDwwLjAwMDIpIGltcHJvYmFibGUuIFRoZXJlZm9yZSwgSSB1c2VkIHRoZSBzZXQgb2YgMTUgcGFsaW5kcm9tZXMgKGxhZz0xNCkgaW4gdGhpcyBhbmFseXNpcy4gR3JhcGggRCBpcyB0aGUgZGVuc2l0eSBwbG90IG9mIHRoZSBnYW1tYSBkaXN0cmlidXRpb24uIEluIGJvdGggZ3JhcGhzLCBwdXJwbGUgYW5kIGJsdWUgbGluZXMgYXJlIHNob3dpbmcgMiUgYW5kIDAuMDIlIHF1YW50aWxlIChleHRyZW1lbHkgc21hbGwgZGlzdGFuY2VzKSBvZiB0aGUgZ2FtbWEgZGlzdHJpYnV0aW9uLiBUaGlzIGluZGljYXRlcyB0aGF0IHRoZXJlIGlzIGFuIGV4dHJlbWVseSBsb3cgcHJvYmFiaWxpdHkgKDwwLjAyKSBvZiB0aGUgb2NjdXJyZW5jZSBvZiAxNSBwYWxpbmRyb21lcyBpbiB0aGVzZSBzbWFsbCBkaXN0YW5jZXMgYnkgY2hhbmNlLiBJZiB3ZSBjb21wYXJlIHRoZSBsb2NhdGlvbnMgb2YgdGhlc2UgdHdvIHJlZ2lvbnMgd2l0aCBncmFwaCBBLCB3ZSBmaW5kIHRoYXQgdGhleSBmYWxsIGluc2lkZSB0aGUgc2ltaWxhciByZWdpb25zIHdlIGRldGVjdGVkIGluIGdyYXBoIEEgd2l0aCA5NSUgcXVhbnRpbGUuIFRoZXNlIHNlZW1pbmdseSB0aWdodCBjbHVzdGVycyAoc2V0cyBvZiAxNSkgb2YgcGFsaW5kcm9tZXMgbWlnaHQgaGF2ZSBzb21lIHNpZ25pZmljYW50IGJpb2xvZ2ljYWwgcm9sZXMsIGZvciBleGFtcGxlLCBtYXkgcG9zc2VzcyB0aGUgb3JpZ2luIG9mIHJlcGxpY2F0aW9uLiAgDQoNCg0KDQpgYGB7ciwgZmlnLmhlaWdodD01fQ0KIyMgc2F2ZSBpbmRleGVzIG9mIGltcHJvYmFibHkgc21hbGwgZGlzdGFuY2VzDQpteXByZWNpb3VzLmluZGV4ZXM8LXdoaWNoKHBnYW1tYShkaWZmKGNtdiwgbGFnPTE0KSwgcmF0ZSA9Mjk2LzIyOTM1NCwgc2hhcGU9MTQpIDwgMC4wMikgIyBDcmVhdGluZyBhIHZlY3RvciB0aGF0IGNvbnRhaW4gaW5kZXhlcyBvZiB0aGUgbG9jYXRpb25zIG9mIDFzdCBwYWxpbmRyb21lcyBvZiBzZXRzIG9mIDE1IHBhbGluZHJvbWVzLCBvZiB3aGljaCwgdGhlIHByb2JhYmlsaXR5IG9mIGRpc3RhbmNlcyBiZXR3ZWVuIDFzdCBhbmQgMTV0aCBwYWxpbmRyb21lIGlzIGxlc3MgdGhhbiAwLjAyIHVzaW5nIHRoZSBjdW11bGF0aXZlIGRlbnNpdHkgb2YgZnVuY3Rpb24sIHBnYW1tYSgpLCBvZiB0aGUgZ2FtbWEgZGlzdHJpYnV0aW9uLiAgIA0KbXlzdHJpY3RwcmVjaW91cy5pbmRleGVzPC13aGljaChwZ2FtbWEoZGlmZihjbXYsIGxhZz0xNCksIHJhdGUgPTI5Ni8yMjkzNTQsIHNoYXBlPTE0KSA8IDAuMDAwMikNCg0KcGFyKG1hcj1jKDUsIDUsIDIsMikpDQpwbG90KGNtdlsxOigyOTYtMTQpXSwgMTooMjk2LTE0KSwgdHlwZT0nbicsICMgQ3JlYXRpbmcgYW4gZW1wdHkgcGxvdC4gDQogICAgIHlsYWI9IlNldHMgb2YgMTUgcGFsaW5kcm9tZXMiLCANCiAgICAgeGxhYj0iTG9jYXRpb25zIGluIHRoZSBDTVYgZ2Vub21lIiwgDQogICAgIG1haW4gPSAiR3JhcGggRTogRGlzdGFuY2VzIGJld2VlbiB0aGUgdHdvIHRlcm1pbmFsIHBhbGluZHJvbWVzIG9mIGVhY2ggc2V0IG9mIDE1IHBhbGluZG9tZXMgYXQgZGlmZmVyZW50IHBvc2l0aW9ucyBpbiBDTVYgZ2Vub21lIiwNCiAgICAgY2V4Lm1haW49IDIuMiwNCiAgICAgY2V4LmxhYj0gMi41LA0KICAgICBjZXguYXhpcyA9IDIuNSwNCiAgICAgbHdkPTMpDQpzZWdtZW50cyhjbXZbMTooMjk2LTE0KV0sIDE6KDI5Ni0xNCksICAjIEFkZCBkaXN0YW5jZXMgYmV0d2VlbiB0d28gdGVybWluYWwgcGFsaW5kcm9tZXMgb2YgZWFjaCBzZXQgb2YgMTUgcGFsaW5kcm9tZXMgYXMgbGluZSBzZWdtZW50cy4NCiAgICAgICAgIGNtdlsoMSsxNCk6Mjk2XSwgMTooMjk2LTE0KSkNCnNlZ21lbnRzKGNtdltteXByZWNpb3VzLmluZGV4ZXNdLCBteXByZWNpb3VzLmluZGV4ZXMsICMgQWRkIGRpc3RhbmNlcyBiZXR3ZWVuIHR3byB0ZXJtaW5hbCBwYWxpbmRyb21lcyBvZiBzZXRzIG9mIHBhbGluZHJvbWVzIGluZGV4ZWQgYnkgbXlwcmVjaW91cyBhcyBsaW5lIHNlZ21lbnRzIHdpdGggcmVkIGNvbG9yLiBUaGlzIHNlZ21lbnRzIG92ZXJyaWRlIHRoZSBwcmV2aW91cyBzZWdtZW50cyBhdCB0aGUgc2FtZSBsb2NhdGlvbnMuIA0KICAgICAgICAgY212W215cHJlY2lvdXMuaW5kZXhlcysxNF0sIG15cHJlY2lvdXMuaW5kZXhlcywgY29sPSJyZWQiLCBsd2QgPSAzKSANCg0Kc2VnbWVudHMoY212W215c3RyaWN0cHJlY2lvdXMuaW5kZXhlc10sIG15c3RyaWN0cHJlY2lvdXMuaW5kZXhlcywgDQogICAgICAgICBjbXZbbXlzdHJpY3RwcmVjaW91cy5pbmRleGVzKzE0XSwgbXlzdHJpY3RwcmVjaW91cy5pbmRleGVzLCBjb2w9IGFkanVzdGNvbG9yKCAieWVsbG93IiwgYWxwaGEuZiA9IDAuNyksIGx3ZCA9IDMpDQpgYGANClRvIHZpc3VhbGl6ZSBkaXN0YW5jZXMgYXMgc2hvd24gaW4gZ3JhcGggQyBtb3JlIGNsZWFybHksIHdlIGRldmVsb3BlZCBHcmFwaCBFLiBJdCBzaG93cyB0aGUgZGlzdGFuY2VzIGJldHdlZW4gdHdvIHRlcm1pbmFsIHBhbGluZHJvbWVzIG9mIHNldHMgb2YgMTUgcGFsaW5kcm9tZXMgYnkgbGluZSBzZWdtZW50cy4gSGVyZSwgZWFjaCBsaW5lIGluZGljYXRlcyBhIHNldCBvZiAxNSBwYWxpbmRyb21lcyBhbmQgdGhlIGxlbmd0aCBvZiB0aGUgbGluZSBpbmRpY2F0ZXMgdGhlIGRpc3RhbmNlIGJldHdlZW4gdGhlIDFzdCBhbmQgMTV0aCBwYWxpbmRyb21lcyBvZiB0aGF0IHNldC4gRnJvbSB0aGUgeC1heGlzLCB3ZSBjYW4gYWxzbyB1bmRlcnN0YW5kIHRoZSBwb3NpdGlvbiBvZiBlYWNoIHNldCBvZiBwYWxpbmRyb21lcyBpbiB0aGUgQ01WIGdlbm9tZS4gV2UgY2FuIHZpc3VhbGl6ZSB0aGF0IHRoZXJlIGFyZSB0d28gcGxhY2VzIGluIHRoZSBnZW5vbWUgKGNvbG9yZWQgd2l0aCBib3RoIHllbGxvdyBhbmQgcmVkKSB3aGVyZSBsZW5ndGhzIG9mIHNldHMgb2YgcGFsaW5kcm9tZXMgYXJlIHNpZ25pZmljYW50bHkgbGVzcyAocHJvYmFiaWxpdHkgaXMgbGVzcyB0aGFuIDAuMDIpIGNvbXBhcmVkIHRvIHRoZSByZXN0IG9mIHRoZSBnZW5vbWUgYXMgYWxzbyBpbmRpY2F0ZWQgaW4gZ3JhcGggQy4gQXMgSSBkaXNjdXNzZWQgYmVmb3JlLCB3aXRoIHRoaXMgZXh0cmVtZWx5IGxvdyBwcm9iYWJpbGl0eSAoPDAuMDIgKHJlZCt5ZWxsb3cgcmVnaW9ucykgIG9yIDwwLjAwMDIgKG9ubHkgeWVsbG93IHJlZ2lvbnMpKSB0aGlzIGlzIGhpZ2hseSB1bmxpa2VseSB0aGF0IHRoZXNlIHBhbGluZHJvbWVzIGFyZSBpbiB0aGVzZSBwb3NpdGlvbnMgb2YgdGhlIGdlbm9tZSBqdXN0IGJ5IGNoYW5jZS4gQmFzZWQgb24gdGhlc2Ugb2JzZXJ2YXRpb25zLCBwcmltYXJpbHkgSSBtYWRlIHR3byByZWNvbW1lbmRhdGlvbnMgYmVsb3cgdG8gc3RhcnQgZXhwZXJpbWVudGFsbHkgc2VhcmNoaW5nIGZvciB0aGUgb3JpZ2luIG9mIHJlcGxpY2F0aW9uLiANCg0KIyMgKkZpbmFsIHJlY29tbWVuZGF0aW9ucyoNCkkgd291bGQgbGlrZSB0byBtYWtlIHR3byBhbHRlcm5hdGUgcmVjb21tZW5kYXRpb25zLiBZb3UgbWF5IHN0YXJ0IHdpdGggYW55IG9uZSBvZiB0aGVzZSBiYXNlZCBvbiB5b3VyIG92ZXJhbGwgZ29hbCwgYXZhaWxhYmxlIHRpbWUsIGFuZCByZXNvdXJjZXMuIA0KDQojIyMgMS4gSWYgeW91IHdhbnQgdG8gc3RhcnQgYW5hbHlzaXMgZm9jdXNpbmcgb24gYnJvYWRlciByZWdpb25zICh3aXRoIHByb2JhYmlsaXR5IDwgMC4wMiksIG1hcmtlZCB3aXRoIHJlZCBhbmQgeWVsbG93IGNvbG9ycyBpbiB0aGUgZ3JhcGggRS4NCmBgYHtyfQ0KbGlicmFyeShrYWJsZUV4dHJhKQ0KDQppbmRleF9jbXYxPC0gd2hpY2gocGdhbW1hKGRpZmYoY212LCBsYWc9MTQpLCByYXRlID0yOTYvMjI5MzU0LCBzaGFwZT0xNCkgPCAwLjAyKSAjIFNhbWUgYXMgbXlwcmVjaW91cy5pbmRleGVzDQoNCmJyb2FkX3JlZ2lvbnMgPC0gZGF0YS5mcmFtZShJbmRleD1OQSwgICMgQ3JlYXRpbmcgYSBkYXRhZnJhbWUgY2FsbGVkIGJyb2FkX3JlZ2lvbnMuDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgU3RhcnQ9TkEsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgRW5kPU5BLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgIERpc3RhbmNlPU5BKQ0KDQpmb3IgKGkgaW4gMTpsZW5ndGgoaW5kZXhfY212MSkpIHsNCiAgbG9jID0gaW5kZXhfY212MVtpXQ0KICANCiAgYnJvYWRfcmVnaW9uc1tpLDFdIDwtIGxvYyAgICAjIGFkZGluZyBpbmRleGVzIHRvIHRoZSBmaXJzdCBjb2x1bW4gb2YgdGhlIGRhdGFmcmFtZS4gDQogIGJyb2FkX3JlZ2lvbnNbaSwyXSA8LSBjbXZbbG9jXSAjIGFkZGluZyBzdGFydGluZyBsb2NhdGlvbnMgb2Ygc2V0cyBvZiAxNSBwYWxpbmRyb21lcyB0byB0aGUgc2Vjb25kIGNvbHVtbiBvZiB0aGUgZGF0YWZyYW1lLiANCiAgYnJvYWRfcmVnaW9uc1tpLDNdIDwtIGNtdltsb2MrMTRdICMgYWRkaW5nIGVuZCBsb2NhdGlvbnMgb2Ygc2V0cyBvZiAxNSBwYWxpbmRyb21lcyB0byB0aGUgdGhpcmQgY29sdW1uIG9mIHRoZSBkYXRhZnJhbWUuIA0KICBicm9hZF9yZWdpb25zW2ksNF0gPC0gY212W2xvYysxNF0gLSBjbXZbbG9jXSAjICMgYWRkaW5nIGRpc3RhbmNlcyBiZXR3ZWVuIHR3byB0ZXJtaW5hbHMgcGFsaW5kcm9tZXMgb2Ygc2V0cyAxNSBwYWxpbmRyb21lcyBpbiB0aGUgZm91cnRoIGNvbHVtbiBvZiB0aGUgZGF0YWZyYW1lLiAgDQogIA0KfQ0KdGJsMTwtIGthYmxlKGJyb2FkX3JlZ2lvbnMsIGNhcHRpb24gPSAiVGFibGUgMTogbG9jYXRpb25zIG9mIHR3byB0ZXJtaW5hbCBwYWxpbmRyb21lcyBhbmQgZGlzdGFuY2VzIGJldHdlZW4gdGhlbSBvZiBzZXRzIG9mIDE1IHBhbGluZHJvbWVzIGluIHRoZSBDTVYgZ2Vub21lIChwcm9iYWJpbGl0eSA8IDAuMDIpIikNCmNvbHVtbl9zcGVjKHRibDEsIDE6NCwgd2lkdGggPSAiMWluIikgIyBjcmVhdGluZyBhIHRhYmxlIHdpdGggYSBjYXB0aW9uIGFuZCBkZWZpbmVkIHdpZHRoIG9mIGNvbHVtbnMuIA0KDQpgYGANCkluIHRoZSB0YWJsZSBhYm92ZSwgZWFjaCByb3cgc2hvd3MgdGhlIDFzdCAoU3RhcnQpIGFuZCAxNXRoIChFbmQpIHBvc2l0aW9ucyBvZiBhIHNldCBvZiAxNSBwYWxpbmRyb21lcyBhbmQgdGhlIGRpc3RhbmNlIChEaXN0YW5jZSkgc3Bhbm5lZCBieSB0aG9zZSBwYWxpbmRyb21lcy4gRnJvbSB0aGUgdGFibGUsIHdlIGZpbmQgdGhhdCB0aGUgbWF4aW11bSBkaXN0YW5jZSBhIHNldCBvZiAxNSBwYWxpbmRyb21lcyBzcGFuIGlzIH41NjY4IGJwLiBUaGUgcHJvYmFiaWxpdHkgb2YgdGhlc2UgZGlzdGFuY2VzIGlzIDwgMC4wMiwgd2hpY2ggaXMgaGlnaGx5IGltcHJvYmFibGUsIHdoaWNoIGluZGljYXRlcyB0aGF0IGNvbXBhcmVkIHRvIG90aGVyIGRpc3RhbmNlcyB3aXRoIGEgcHJvYmFiaWxpdHkgPiAwLjAyLCB0aGVzZSBkaXN0YW5jZXMgYXJlIHNpZ25pZmljYW50bHkgc21hbGxlci4gU28sIHdlIGNhbiBzYXkgdGhhdCAxNSBwYWxpbmRyb21lcyBpbiBhIHNldCBhcmUgdGlnaHRseSBwYWNrZWQgdG9nZXRoZXIgaW4gdGhlc2UgcG9zaXRpb25zLiBBbmQgdGhpcyBpcyBoaWdobHkgdW5saWtlbHkgdGhhdCB0aGV5IHBhY2tlZCB0b2dldGhlciBieSBjaGFuY2UgKHdpdGggdGhlIHByb2JhYmlsaXR5IG9mIDAuMDIpLiBUaGlzIHNob3dzIGEgY2xlYXIgZGV2aWF0aW9uIGZyb20gdGhlIGhvbW9nZW5lb3VzIFBvaXNzb24gcHJvY2Vzcy4gVGhlcmVmb3JlLCBhbnkgb25lIG9mIHRoZXNlIHNldHMgb3IgYSBncm91cCBvZiB0aGVzZSBzZXRzIG9mIDE1IHBhbGluZHJvbWVzIG1heSBwb3NzZXNzIHRoZSBzaXRlIG9mIHRoZSBvcmlnaW4gb2YgcmVwbGljYXRpb24uIEJhc2VkIG9uIHRoZSBjb25zZWN1dGl2ZSBzZXRzIG9mIHBhbGluZHJvbWVzIChzZWUgSW5kZXggaW4gdGFibGUgMSksIEkgd291bGQgc3VnZ2VzdCBzdGFydGluZyB0aGUgYW5hbHlzaXMgZm9jdXNpbmcgb24gdGhlc2UgdHdvIHJlZ2lvbnMsIDg3NzE3IGJwIC0gOTc0ODggYnAsIGFuZCAxODk4MTAgYnAgLSAxOTg3MDkgYnAgKHRhYmxlIDEpLiAgIA0KDQojIyMgMi4gIElmIHlvdSB3YW50IHRvIHN0YXJ0IHRoZSBhbmFseXNpcyBieSBmb2N1c2luZyBvbiByZWdpb25zIHdpdGggZXh0cmVtZWx5IGltcHJvYmFibGUgZGlzdGFuY2VzIChwcm9iYWJpbGl0eSA8IDAuMDAwMiksIG1hcmtlZCB3aXRoIG9ubHkgeWVsbG93IGluIHRoZSBncmFwaCBFLiAgDQpgYGB7cn0NCg0KaW5kZXhfY212MjwtIHdoaWNoKHBnYW1tYShkaWZmKGNtdiwgbGFnPTE0KSwgcmF0ZSA9Mjk2LzIyOTM1NCwgc2hhcGU9MTQpIDwgMC4wMDAyKSAjIHNhbWUgYXMgYWJvdmUNCg0Kc21hbGxlcl9yZWdpb25zIDwtIGRhdGEuZnJhbWUoSW5kZXg9TkEsICAgI3NhbWUgYXMgYWJvdmUNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBTdGFydD1OQSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBFbmQ9TkEsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgRGlzdGFuY2U9TkEpDQoNCmZvciAoaSBpbiAxOmxlbmd0aChpbmRleF9jbXYyKSkgew0KICBsb2MgPSBpbmRleF9jbXYyW2ldDQogIA0KICBzbWFsbGVyX3JlZ2lvbnNbaSwxXSA8LSBsb2MgICAgICAgICAgICAgICAjc2FtZSBhcyBhYm92ZS4NCiAgc21hbGxlcl9yZWdpb25zW2ksMl0gPC0gY212W2xvY10NCiAgc21hbGxlcl9yZWdpb25zW2ksM10gPC0gY212W2xvYysxNF0NCiAgc21hbGxlcl9yZWdpb25zW2ksNF0gPC0gY212W2xvYysxNF0gLSBjbXZbbG9jXQ0KfQ0KdGJsMiA8LSBrYWJsZShzbWFsbGVyX3JlZ2lvbnMsIGNhcHRpb24gPSAiVGFibGUgMjogbG9jYXRpb25zIG9mIHR3byB0ZXJtaW5hbCBwYWxpbmRyb21lcyBhbmQgZGlzdGFuY2VzIGJldHdlZW4gdGhlbSBvZiBzZXRzIG9mIDE1IHBhbGluZHJvbWVzIGluIHRoZSBDTVYgZ2Vub21lIChwcm9iYWJpbGl0eSA8IDAuMDAwMikiKQ0KY29sdW1uX3NwZWModGJsMiwgMTo0LCB3aWR0aCA9ICIxaW4iKQ0KDQpgYGANCklmIHlvdSB3YW50IHRvIHN0YXJ0IHNlYXJjaGluZyBmb3IgdGhlIG9yaWdpbiBvZiByZXBsaWNhdGlvbiB3aXRoaW4gc21hbGxlciByZWdpb25zLCB5b3UgY2FuIHN0YXJ0IHdpdGggdGhlc2UgcmVnaW9ucyBhcyBpbmRpY2F0ZWQgaW4gdGFibGUgMi4gVGhlIGV4YWN0IGxvY2F0aW9ucyBvZiBmb3VyIHNldHMgb2YgZXh0cmVtZWx5IHBhY2tlZCAxNSBwYWxpbmRyb21lcyBhcmUgZ2l2ZW4gaGVyZS4gVGhlIG1heGltdW0gZGlzdGFuY2UgYmV0d2VlbiB0d28gdGVybWluYWwgcGFsaW5kcm9tZXMgb2YgYSBzZXQgaXMgIDI5OTkgYnAgYW5kIHRoZSBtaW5pbXVtIGlzIG9ubHkgMjExMSBicCwgd2hpY2ggaXMgZXh0cmVtZWx5IHVubGlrZWx5ICh3aXRoIHByb2JhYmlsaXR5IDwgMC4wMDAyKSB0aGF0IHRoZSBwYWxpbmRyb21lcyBpbiBhIHNldCBhcmUgdmVyeSB0aWdodGx5IGNsdXN0ZXJlZCB0b2dldGhlciBqdXN0IGJ5IGNoYW5jZS4gVGhlc2UgZXh0cmVtZWx5IHNtYWxsIGRpc3RhbmNlcyBhbHNvIHNob3cgYSBoaWdobHkgc2lnbmlmaWNhbnQgZGV2aWF0aW9uIGZyb20gdGhlIFBvaXNzb24gcHJvY2Vzcy4gVGhlcmVmb3JlLCByZWdpb24gZnJvbSA5MDI1MSBicCB0byA5NDE3NCBicCBoYXMgdGhlIGhpZ2hlc3QgcHJvYmFiaWxpdHkgdG8gaGF2ZSB0aGUgc2l0ZSBvZiB0aGUgb3JpZ2luIG9mIHJlcGxpY2F0aW9uLiBTbywgaXQgaXMgd29ydGggc3RhcnRpbmcgdG8gc2VhcmNoIHdpdGggdGhlc2UgcmVnaW9ucyBmb3IgdGhlIG9yaWdpbiBvZiByZXBsaWNhdGlvbiBpbiB0aGUgbGFib3JhdG9yeSBhbmQgaWYgbmVjZXNzYXJ5LCBleHBsb3JlIHRoZSBvdGhlciByZWdpb25zIGFzIG1lbnRpb25lZCBpbiB0YWJsZSAxLiAgIA0KDQoNCg0KDQoNCg==