1 M. musculus

This will be a very minimal analysis until we get some replicates.

1.2 Metadata

I am going to write a quick sample sheet in the current working directory called ‘all_samples.xlsx’ and put the names of the count tables in it.

1.3 Create expressionsets

Here I combine the metadata, count data, and annotations.

It is worth noting that the gene IDs from htseq-count probably do not match the annotations retrieved because they are likely exon-based rather than gene based. This is not really a problem, but don’t forget it!

## Reading the sample metadata.
## The sample definitions comprises: 8 rows(samples) and 8 columns(metadata fields).
## Reading count tables.
## Reading count tables with read.table().
## /mnt/sshfs/cbcbsub/fs/cbcb-lab/nelsayed/scratch/atb/rnaseq/mmusculus_iprgc_2019/preprocessing/iprgc_01/outputs/hisat2_mm38_95/r1_trimmed.count.xz contains 25788 rows.
## /mnt/sshfs/cbcbsub/fs/cbcb-lab/nelsayed/scratch/atb/rnaseq/mmusculus_iprgc_2019/preprocessing/iprgc_02/outputs/hisat2_mm38_95/r1_trimmed.count.xz contains 25788 rows and merges to 25788 rows.
## /mnt/sshfs/cbcbsub/fs/cbcb-lab/nelsayed/scratch/atb/rnaseq/mmusculus_iprgc_2019/preprocessing/iprgc_03/outputs/hisat2_mm38_95/r1_trimmed.count.xz contains 25788 rows and merges to 25788 rows.
## /mnt/sshfs/cbcbsub/fs/cbcb-lab/nelsayed/scratch/atb/rnaseq/mmusculus_iprgc_2019/preprocessing/iprgc_04/outputs/hisat2_mm38_95/r1_trimmed.count.xz contains 25788 rows and merges to 25788 rows.
## /mnt/sshfs/cbcbsub/fs/cbcb-lab/nelsayed/scratch/atb/rnaseq/mmusculus_iprgc_2019/preprocessing/iprgc_05/outputs/hisat2_mm38_95/r1_trimmed.count.xz contains 25788 rows and merges to 25788 rows.
## /mnt/sshfs/cbcbsub/fs/cbcb-lab/nelsayed/scratch/atb/rnaseq/mmusculus_iprgc_2019/preprocessing/iprgc_06/outputs/hisat2_mm38_95/r1_trimmed.count.xz contains 25788 rows and merges to 25788 rows.
## /mnt/sshfs/cbcbsub/fs/cbcb-lab/nelsayed/scratch/atb/rnaseq/mmusculus_iprgc_2019/preprocessing/iprgc_07/outputs/hisat2_mm38_95/r1_trimmed.count.xz contains 25788 rows and merges to 25788 rows.
## /mnt/sshfs/cbcbsub/fs/cbcb-lab/nelsayed/scratch/atb/rnaseq/mmusculus_iprgc_2019/preprocessing/iprgc_08/outputs/hisat2_mm38_95/r1_trimmed.count.xz contains 25788 rows and merges to 25788 rows.
## Finished reading count tables.
## Matched 25554 annotations and counts.
## Bringing together the count matrix and gene information.
## Some annotations were lost in merging, setting them to 'undefined'.
## The final expressionset has 25783 rows and 8 columns.

1.4 Query expressionsets

In this block I will calculate all the diagnostic plots, but not show them. I will show them next with a little annotation.

I will leave the output for the first of each invocation and silence it for the second.

1.4.1 Initial salmon plots

## This function will replace the expt$expressionset slot with:
## log2(cpm(quant(cbcb(data))))
## It will save copies of each step along the way
##  in expt$normalized with the corresponding libsizes. Keep libsizes in mind
##  when invoking limma.  The appropriate libsize is non-log(cpm(normalized)).
##  This is most likely kept at:
##  'new_expt$normalized$intermediate_counts$normalization$libsizes'
##  A copy of this may also be found at:
##  new_expt$best_libsize
## Not correcting the count-data for batch effects.  If batch is
##  included in EdgerR/limma's model, then this is probably wise; but in extreme
##  batch effects this is a good parameter to play with.
## Step 1: performing count filter with option: cbcb
## Removing 2794 low-count genes (3970 remaining).
## Step 2: normalizing the data with quant.
## Step 3: converting the data with cpm.
## Step 4: transforming the data with log2.
## transform_counts: Found 1105 values equal to 0, adding 1 to the matrix.
## Step 5: not doing batch correction.

1.4.2 Initial hisat2 plots

## This function will replace the expt$expressionset slot with:
## log2(cpm(quant(cbcb(data))))
## It will save copies of each step along the way
##  in expt$normalized with the corresponding libsizes. Keep libsizes in mind
##  when invoking limma.  The appropriate libsize is non-log(cpm(normalized)).
##  This is most likely kept at:
##  'new_expt$normalized$intermediate_counts$normalization$libsizes'
##  A copy of this may also be found at:
##  new_expt$best_libsize
## Not correcting the count-data for batch effects.  If batch is
##  included in EdgerR/limma's model, then this is probably wise; but in extreme
##  batch effects this is a good parameter to play with.
## Step 1: performing count filter with option: cbcb
## Removing 12233 low-count genes (13550 remaining).
## Step 2: normalizing the data with quant.
## Step 3: converting the data with cpm.
## Step 4: transforming the data with log2.
## transform_counts: Found 19 values equal to 0, adding 1 to the matrix.
## Step 5: not doing batch correction.
## Error in plot_nonzero(expt, title = nonzero_title, ...) : 
##   object 'mm38_norm' not found
## Error in plot_libsize(expt, title = libsize_title, ...) : 
##   object 'mm38_norm' not found
## Error in plot_boxplot(expt, title = boxplot_title, ...) : 
##   object 'mm38_norm' not found
## Error in plot_heatmap(expt_data, expt_colors = expt_colors, expt_design = expt_design,  : 
##   object 'mm38_norm' not found
## Error in plot_sm(expt, method = cormethod, title = smc_title, ...) : 
##   object 'mm38_norm' not found
## Error in plot_heatmap(expt_data, expt_colors = expt_colors, expt_design = expt_design,  : 
##   object 'mm38_norm' not found
## Error in plot_sm(expt, method = distmethod, title = smd_title, ...) : 
##   object 'mm38_norm' not found
## Error in plot_pca(expt, title = pca_title, ...) : 
##   object 'mm38_norm' not found
## Error in plot_pca(..., pc_method = "tsne") : object 'mm38_norm' not found
## Error in plot_density(expt, title = dens_title, ...) : 
##   object 'mm38_norm' not found
## Error in plot_variance_coefficients(expt, title = dens_title, ...) : 
##   object 'mm38_norm' not found
## Error in plot_topn(expt, title = topn_title, ...) : 
##   object 'mm38_norm' not found
## Error in plot_nonzero(expt, title = nonzero_title, ...) : 
##   object 'mm38_norm' not found
## Error in plot_libsize(expt, title = libsize_title, ...) : 
##   object 'mm38_norm' not found
## Error in plot_boxplot(expt, title = boxplot_title, ...) : 
##   object 'mm38_norm' not found
## Error in plot_heatmap(expt_data, expt_colors = expt_colors, expt_design = expt_design,  : 
##   object 'mm38_norm' not found
## Error in plot_sm(expt, method = cormethod, title = smc_title, ...) : 
##   object 'mm38_norm' not found
## Error in plot_heatmap(expt_data, expt_colors = expt_colors, expt_design = expt_design,  : 
##   object 'mm38_norm' not found
## Error in plot_sm(expt, method = distmethod, title = smd_title, ...) : 
##   object 'mm38_norm' not found
## Error in plot_pca(expt, title = pca_title, ...) : 
##   object 'mm38_norm' not found
## Error in plot_pca(..., pc_method = "tsne") : object 'mm38_norm' not found
## Error in plot_density(expt, title = dens_title, ...) : 
##   object 'mm38_norm' not found
## Error in plot_variance_coefficients(expt, title = dens_title, ...) : 
##   object 'mm38_norm' not found
## Error in plot_topn(expt, title = topn_title, ...) : 
##   object 'mm38_norm' not found
## Error in normalize_expt(expt, filter = TRUE): object 'mm38_norm' not found

## Error in eval(expr, envir, enclos): object 'mm38n_plots_hi' not found
## Error in eval(expr, envir, enclos): object 'mm38n_plots_hi' not found

1.5 Do a simple DE

The only interesting DE I see in this is to compare the retinas to the dlgns. I can treat them as replicates and compare.

These differential expression analyses are EXPLICITLY NOT what you care about. However, they are useful for two purposes:

  1. Seeing that the three tissue types are indeed different.
  2. Setting up the table of results with appropriate rows/columns of (rows)genes and (columns) annotations. We will therefore add to these tables the results of the expression analyses that you actually do care about.

When we receive full replicate sets, this cheater method of encapsulating the data will not longer be required.

1.5.1 With salmon

## This function will replace the expt$expressionset slot with:
## log2(cpm(quant(cbcb(data))))
## It will save copies of each step along the way
##  in expt$normalized with the corresponding libsizes. Keep libsizes in mind
##  when invoking limma.  The appropriate libsize is non-log(cpm(normalized)).
##  This is most likely kept at:
##  'new_expt$normalized$intermediate_counts$normalization$libsizes'
##  A copy of this may also be found at:
##  new_expt$best_libsize
## Not correcting the count-data for batch effects.  If batch is
##  included in EdgerR/limma's model, then this is probably wise; but in extreme
##  batch effects this is a good parameter to play with.
## Step 1: performing count filter with option: cbcb
## Removing 2794 low-count genes (3970 remaining).
## Step 2: normalizing the data with quant.
## Step 3: converting the data with cpm.
## Step 4: transforming the data with log2.
## transform_counts: Found 1105 values equal to 0, adding 1 to the matrix.
## Step 5: not doing batch correction.
## Plotting a PCA before surrogates/batch inclusion.
## Not putting labels on the plot.
## Assuming no batch in model for testing pca.
## Not putting labels on the plot.
## Finished running DE analyses, collecting outputs.
## Comparing analyses.

1.6 Set up for initial analysis

Until we get full replicates, I will do simple subtractions.

1.6.1 Term definition

In an attempt to keep some clarity in the terms used, I want to define them now. There are three contexts in which we will consider the data:

  1. The individual sample type. When considering individual samples, I will use three terms in this and only this context: wild-type (wt), het, and mut.

  2. The individual translatome. These are defines as something / baseline. I will exclusively call the wt samples ‘baseline’ when speaking in this context. I will exclusively state ‘normal’ when referring to het / wt samples, and I will state ‘ko’ when referring to mut / wt samples in the translatome context.

  3. Translatome vs. translatome. Whenever comparing translatomes, I will use the names as in #2 and always put the numerator first when writing the name of a comparison.

The most complex example of the above nomenclature is:

“normko_retdlgn is defined as normret_vs_normdlgn - koret_vs_kodlgn”

This states we are examining at the translatome context: (norm(retina translatome) - norm(dlgn translatome)) - (ko(retina translatome) - ko(dlgn translatome))

Which in turn is synonymous to the following at the sample context: ((rethet - retwt) - (dlgnhet - dlgnwt)) - ((retko - retwt) - (dlgnko - dlgnwt))

Now let us associate the various variable names with the appropriate samples:

Give these variable names, now lets associate columns of the expression data with them. These are at the sample context, so the appropriate names are: ‘wt’, ‘het’, and ‘mut’. In each case I will prefix the genotype with the tissue type: ‘ret’, ‘dlgn’, and ‘scn’. Thus ‘retwt’ refers to the sample used to calculate the translatome retina baseline; in contrast ‘dlgnmut’ is the sample which provides the dlgn knockout.

Each of the above 8 variables provides 1 column of information. We have 3 baseline comparisons available to us. In each of these we compare one wt sample to another.

Simultaneously, we have 5 available translatomes. This are provided by comparing each het or mut to the associated wt. These will therefore receive names: ‘norm’ and ‘ko’ instead of ‘het’ and ‘mut’.

Given these translatomes, there are a few contrasts of likely interest. These are performed by comparing the relevant translatomes.

Will will split these into 4 separate categories: het vs het, ko vs ko, ko vs het, and ratio vs ratio.

Finally, note that we are being explicitly redundant in these definitions. I am making variable names for both the a/b ratio and the b/a ratio. Thus we have some redundantly redundant (haha) flexibility when deciding on what we want to plot.

On the other hand, I am assuming we always want the normals as denominators and kos as numerators.

Finally, here is the ratio of ratios example I printed above:

I named it ‘normko_retdlgn’ in an attempt to make clear that it is actually: (normret/normdlgn)/(koret/kodlgn)

or stated differently: “norm divided by ko for ret divided by dlgn.”

1.9 Add the matrix to the differential expression

I will use my function combine_de_tables() to add this information to my existing annotation data along with the results from the statistically valid comparison of the three tissue types.

2 Plots of interesting comparisons

## Warning: Removed 37 rows containing missing values (geom_point).

## Warning: Removed 37 rows containing missing values (geom_point).

3 Some pictures

As I understand it, there is some interest in an ontology search using the ratio of ratios.

## [1] 1877
## [1] 476
## Performing gProfiler GO search of 1877 genes against mmusculus.
## GO search found 271 hits.
## Performing gProfiler KEGG search of 1877 genes against mmusculus.
## KEGG search found 8 hits.
## Performing gProfiler REAC search of 1877 genes against mmusculus.
## REAC search found 11 hits.
## Performing gProfiler MI search of 1877 genes against mmusculus.
## MI search found 0 hits.
## Performing gProfiler TF search of 1877 genes against mmusculus.
## TF search found 376 hits.
## Performing gProfiler CORUM search of 1877 genes against mmusculus.
## CORUM search found 1 hits.
## Performing gProfiler HP search of 1877 genes against mmusculus.
## HP search found 5 hits.
## Writing data to: excel/20200114mm_ror_gpfoiler_up-v20200114.xlsx.
## Error in `levels<-`(`*tmp*`, value = as.character(levels)) : 
##   factor level [29] is duplicated
## Error in `levels<-`(`*tmp*`, value = as.character(levels)) : 
##   factor level [29] is duplicated
## Error in `levels<-`(`*tmp*`, value = as.character(levels)) : 
##   factor level [29] is duplicated
## Error in `levels<-`(`*tmp*`, value = as.character(levels)) : 
##   factor level [29] is duplicated
## Finished writing data.

## Error in `levels<-`(`*tmp*`, value = as.character(levels)): factor level [29] is duplicated

## Performing gProfiler GO search of 476 genes against mmusculus.
## GO search found 82 hits.
## Performing gProfiler KEGG search of 476 genes against mmusculus.
## KEGG search found 7 hits.
## Performing gProfiler REAC search of 476 genes against mmusculus.
## REAC search found 2 hits.
## Performing gProfiler MI search of 476 genes against mmusculus.
## MI search found 0 hits.
## Performing gProfiler TF search of 476 genes against mmusculus.
## TF search found 6 hits.
## Performing gProfiler CORUM search of 476 genes against mmusculus.
## CORUM search found 0 hits.
## Performing gProfiler HP search of 476 genes against mmusculus.
## HP search found 0 hits.
## Writing data to: excel/20200114mm_ror_gpfoiler_down-v20200114.xlsx.
## Finished writing data.

LS0tCnRpdGxlOiAiTS4gbXVzY3VsdXMgMyBjZWxsIHR5cGVzLCAxIHRpbWVwb2ludCwgMyBnZW5vdHlwZXMsIGFuZCAxIHJlcGxpY2F0ZS4iCmF1dGhvcjogImF0YiBhYmVsZXdAZ21haWwuY29tIgpkYXRlOiAiYHIgU3lzLkRhdGUoKWAiCm91dHB1dDoKICBodG1sX2RvY3VtZW50OgogICAgY29kZV9kb3dubG9hZDogdHJ1ZQogICAgY29kZV9mb2xkaW5nOiBzaG93CiAgICBmaWdfY2FwdGlvbjogdHJ1ZQogICAgZmlnX2hlaWdodDogNwogICAgZmlnX3dpZHRoOiA3CiAgICBoaWdobGlnaHQ6IHRhbmdvCiAgICBrZWVwX21kOiBmYWxzZQogICAgbW9kZTogc2VsZmNvbnRhaW5lZAogICAgbnVtYmVyX3NlY3Rpb25zOiB0cnVlCiAgICBzZWxmX2NvbnRhaW5lZDogdHJ1ZQogICAgdGhlbWU6IHJlYWRhYmxlCiAgICB0b2M6IHRydWUKICAgIHRvY19mbG9hdDoKICAgICAgY29sbGFwc2VkOiBmYWxzZQogICAgICBzbW9vdGhfc2Nyb2xsOiBmYWxzZQogIHJtZGZvcm1hdHM6OnJlYWR0aGVkb3duOgogICAgY29kZV9kb3dubG9hZDogdHJ1ZQogICAgY29kZV9mb2xkaW5nOiBzaG93CiAgICBkZl9wcmludDogcGFnZWQKICAgIGZpZ19jYXB0aW9uOiB0cnVlCiAgICBmaWdfaGVpZ2h0OiA3CiAgICBmaWdfd2lkdGg6IDcKICAgIGhpZ2hsaWdodDogdGFuZ28KICAgIHdpZHRoOiAzMDAKICAgIGtlZXBfbWQ6IGZhbHNlCiAgICBtb2RlOiBzZWxmY29udGFpbmVkCiAgICB0b2NfZmxvYXQ6IHRydWUKICBCaW9jU3R5bGU6Omh0bWxfZG9jdW1lbnQ6CiAgICBjb2RlX2Rvd25sb2FkOiB0cnVlCiAgICBjb2RlX2ZvbGRpbmc6IHNob3cKICAgIGZpZ19jYXB0aW9uOiB0cnVlCiAgICBmaWdfaGVpZ2h0OiA3CiAgICBmaWdfd2lkdGg6IDcKICAgIGhpZ2hsaWdodDogdGFuZ28KICAgIGtlZXBfbWQ6IGZhbHNlCiAgICBtb2RlOiBzZWxmY29udGFpbmVkCiAgICB0b2NfZmxvYXQ6IHRydWUKLS0tCgo8c3R5bGUgdHlwZT0idGV4dC9jc3MiPgpib2R5LCB0ZCB7CiAgZm9udC1zaXplOiAxNnB4Owp9CmNvZGUucnsKICBmb250LXNpemU6IDE2cHg7Cn0KcHJlIHsKIGZvbnQtc2l6ZTogMTZweAp9Cjwvc3R5bGU+CgpgYGB7ciBvcHRpb25zLCBpbmNsdWRlPUZBTFNFfQpsaWJyYXJ5KCJocGdsdG9vbHMiKQp0dCA8LSBkZXZ0b29sczo6bG9hZF9hbGwoIi9kYXRhL2hwZ2x0b29scyIpCmtuaXRyOjpvcHRzX2tuaXQkc2V0KHdpZHRoPTEyMCwKICAgICAgICAgICAgICAgICAgICAgcHJvZ3Jlc3M9VFJVRSwKICAgICAgICAgICAgICAgICAgICAgdmVyYm9zZT1UUlVFLAogICAgICAgICAgICAgICAgICAgICBlY2hvPVRSVUUpCmtuaXRyOjpvcHRzX2NodW5rJHNldChlcnJvcj1UUlVFLAogICAgICAgICAgICAgICAgICAgICAgZHBpPTk2KQpvbGRfb3B0aW9ucyA8LSBvcHRpb25zKGRpZ2l0cz00LAogICAgICAgICAgICAgICAgICAgICAgIHN0cmluZ3NBc0ZhY3RvcnM9RkFMU0UsCiAgICAgICAgICAgICAgICAgICAgICAga25pdHIuZHVwbGljYXRlLmxhYmVsPSJhbGxvdyIpCmdncGxvdDI6OnRoZW1lX3NldChnZ3Bsb3QyOjp0aGVtZV9idyhiYXNlX3NpemU9MTApKQpydW5kYXRlIDwtIGZvcm1hdChTeXMuRGF0ZSgpLCBmb3JtYXQ9IiVZJW0lZCIpCnByZXZpb3VzX2ZpbGUgPC0gInVuZGVmaW5lZC5SbWQiCnZlciA8LSAiMjAyMDAxMTQiCgojI3RtcCA8LSBzbShsb2FkbWUoZmlsZW5hbWU9cGFzdGUwKGdzdWIocGF0dGVybj0iXFwuUm1kIiwgcmVwbGFjZT0iIiwgeD1wcmV2aW91c19maWxlKSwgIi12IiwgdmVyLCAiLnJkYS54eiIpKSkKcm1kX2ZpbGUgPC0gImluZGV4LlJtZCIKYGBgCgojIE0uIG11c2N1bHVzCgpUaGlzIHdpbGwgYmUgYSB2ZXJ5IG1pbmltYWwgYW5hbHlzaXMgdW50aWwgd2UgZ2V0IHNvbWUgcmVwbGljYXRlcy4KCiMjIEFubm90YXRpb25zCgpJIGFtIHVzaW5nIG1tMzhfOTUuCgpgYGB7ciBhbm5vdGF0aW9uc30KIyMgTXkgbG9hZF9iaW9tYXJ0X2Fubm90YXRpb25zKCkgZnVuY3Rpb24gZGVmYXVsdHMgdG8gaHVtYW4sIHNvIHRoYXQgd2lsbCBiZSBxdWljay4KbW1fYW5ub3QgPC0gbG9hZF9iaW9tYXJ0X2Fubm90YXRpb25zKHNwZWNpZXM9Im1tdXNjdWx1cyIpCm1tX2Fubm90IDwtIG1tX2Fubm90W1siYW5ub3RhdGlvbiJdXQptbV9hbm5vdFtbInR4aWQiXV0gPC0gcGFzdGUwKG1tX2Fubm90W1siZW5zZW1ibF90cmFuc2NyaXB0X2lkIl1dLCAiLiIsIG1tX2Fubm90W1sidmVyc2lvbiJdXSkKcm93bmFtZXMobW1fYW5ub3QpIDwtIG1ha2UubmFtZXMobW1fYW5ub3RbWyJlbnNlbWJsX2dlbmVfaWQiXV0sIHVuaXF1ZT1UUlVFKQoKdHhfZ2VuZV9tYXAgPC0gbW1fYW5ub3RbLCBjKCJ0eGlkIiwgImVuc2VtYmxfZ2VuZV9pZCIpXQpgYGAKClNvLCBJIG5vdyBoYXZlIDIgZGF0YSBmcmFtZXMgb2YgcGFyYXNpdGUgYW5ub3RhdGlvbnMgYW5kIDEgaHVtYW4uCgojIyBNZXRhZGF0YQoKSSBhbSBnb2luZyB0byB3cml0ZSBhIHF1aWNrIHNhbXBsZSBzaGVldCBpbiB0aGUgY3VycmVudCB3b3JraW5nIGRpcmVjdG9yeSBjYWxsZWQKJ2FsbF9zYW1wbGVzLnhsc3gnIGFuZCBwdXQgdGhlIG5hbWVzIG9mIHRoZSBjb3VudCB0YWJsZXMgaW4gaXQuCgojIyBDcmVhdGUgZXhwcmVzc2lvbnNldHMKCkhlcmUgSSBjb21iaW5lIHRoZSBtZXRhZGF0YSwgY291bnQgZGF0YSwgYW5kIGFubm90YXRpb25zLgoKSXQgaXMgd29ydGggbm90aW5nIHRoYXQgdGhlIGdlbmUgSURzIGZyb20gaHRzZXEtY291bnQgcHJvYmFibHkgZG8gbm90IG1hdGNoIHRoZQphbm5vdGF0aW9ucyByZXRyaWV2ZWQgYmVjYXVzZSB0aGV5IGFyZSBsaWtlbHkgZXhvbi1iYXNlZCByYXRoZXIgdGhhbiBnZW5lCmJhc2VkLiAgVGhpcyBpcyBub3QgcmVhbGx5IGEgcHJvYmxlbSwgYnV0IGRvbid0IGZvcmdldCBpdCEKCmBgYHtyIGV4cHR9Cm1tMzhfc2FsbW9uIDwtIHNtKGNyZWF0ZV9leHB0KCJzYW1wbGVfc2hlZXRzL2FsbF9zYW1wbGVzLnhsc3giLCB0eF9nZW5lX21hcD10eF9nZW5lX21hcCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZ2VuZV9pbmZvPW1tX2Fubm90LCBmaWxlX2NvbHVtbj0ic2FsbW9uZmlsZSIpKQoKbW10eF9hbm5vdCA8LSBtbV9hbm5vdApyb3duYW1lcyhtbXR4X2Fubm90KSA8LSBtbV9hbm5vdFtbInR4aWQiXV0KbW0zOF9zYWx0eCA8LSBzbShjcmVhdGVfZXhwdCgic2FtcGxlX3NoZWV0cy9hbGxfc2FtcGxlcy54bHN4IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBnZW5lX2luZm89bW10eF9hbm5vdCwgZmlsZV9jb2x1bW49InNhbG1vbmZpbGUiKSkKCmhpc2F0X2Fubm90IDwtIG1tX2Fubm90CnJvd25hbWVzKGhpc2F0X2Fubm90KSA8LSBwYXN0ZTAoImdlbmUuIiwgcm93bmFtZXMoaGlzYXRfYW5ub3QpKQptbTM4X2hpc2F0IDwtIGNyZWF0ZV9leHB0KCJzYW1wbGVfc2hlZXRzL2FsbF9zYW1wbGVzLnhsc3giLAogICAgICAgICAgICAgICAgICAgICAgICAgIGdlbmVfaW5mbz1oaXNhdF9hbm5vdCkKYGBgCgojIyBRdWVyeSBleHByZXNzaW9uc2V0cwoKSW4gdGhpcyBibG9jayBJIHdpbGwgY2FsY3VsYXRlIGFsbCB0aGUgZGlhZ25vc3RpYyBwbG90cywgYnV0IG5vdCBzaG93IHRoZW0uICBJCndpbGwgc2hvdyB0aGVtIG5leHQgd2l0aCBhIGxpdHRsZSBhbm5vdGF0aW9uLgoKSSB3aWxsIGxlYXZlIHRoZSBvdXRwdXQgZm9yIHRoZSBmaXJzdCBvZiBlYWNoIGludm9jYXRpb24gYW5kIHNpbGVuY2UgaXQgZm9yIHRoZSBzZWNvbmQuCgojIyMgSW5pdGlhbCBzYWxtb24gcGxvdHMKCmBgYHtyIHF1ZXJ5X3NhbG1vbiwgZmlnLnNob3c9ImhpZGUifQptbTM4X3Bsb3RzX3NhIDwtIHNtKGdyYXBoX21ldHJpY3MobW0zOF9zYWxtb24pKQoKbW0zOF9ub3JtX3NhIDwtIG5vcm1hbGl6ZV9leHB0KG1tMzhfc2FsbW9uLCBub3JtPSJxdWFudCIsIGNvbnZlcnQ9ImNwbSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICB0cmFuc2Zvcm09ImxvZzIiLCBmaWx0ZXI9VFJVRSkKCm1tMzhuX3Bsb3RzX3NhIDwtIHNtKGdyYXBoX21ldHJpY3MobW0zOF9ub3JtX3NhKSkKYGBgCgpgYGB7ciBzaG93X3Bsb3RzX3NhbG1vbn0KbW0zOF9wbG90c19zYSRsZWdlbmQKbW0zOF9wbG90c19zYSRsaWJzaXplCm1tMzhfcGxvdHNfc2Ekbm9uemVybwptbTM4bl9wbG90c19zYSRkZW5zaXR5Cm1tMzhuX3Bsb3RzX3NhJHBjX3Bsb3QKYGBgCgojIyMgSW5pdGlhbCBoaXNhdDIgcGxvdHMKCmBgYHtyIHF1ZXJ5X2hpc2F0LCBmaWcuc2hvdz0iaGlkZSJ9Cm1tMzhfcGxvdHNfaGkgPC0gc20oZ3JhcGhfbWV0cmljcyhtbTM4X2hpc2F0KSkKCm1tMzhfbm9ybV9oaSA8LSBub3JtYWxpemVfZXhwdChtbTM4X2hpc2F0LCBub3JtPSJxdWFudCIsIGNvbnZlcnQ9ImNwbSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB0cmFuc2Zvcm09ImxvZzIiLCBmaWx0ZXI9VFJVRSkKCm1tMzhuX3Bsb3RzX2hpIDwtIHNtKGdyYXBoX21ldHJpY3MobW0zOF9ub3JtKSkKYGBgCgpgYGB7ciBzaG93X3Bsb3RzX2hpc2F0fQptbTM4X3Bsb3RzX2hpJGxpYnNpemUKbW0zOF9wbG90c19oaSRub256ZXJvCm1tMzhuX3Bsb3RzX2hpJGRlbnNpdHkKbW0zOG5fcGxvdHNfaGkkcGNfcGxvdApgYGAKCiMjIERvIGEgc2ltcGxlIERFCgpUaGUgb25seSBpbnRlcmVzdGluZyBERSBJIHNlZSBpbiB0aGlzIGlzIHRvIGNvbXBhcmUgdGhlIHJldGluYXMgdG8gdGhlIGRsZ25zLgpJIGNhbiB0cmVhdCB0aGVtIGFzIHJlcGxpY2F0ZXMgYW5kIGNvbXBhcmUuCgpUaGVzZSBkaWZmZXJlbnRpYWwgZXhwcmVzc2lvbiBhbmFseXNlcyBhcmUgX0VYUExJQ0lUTFlfIF9OT1RfIHdoYXQgeW91IGNhcmUKYWJvdXQuICBIb3dldmVyLCB0aGV5IGFyZSB1c2VmdWwgZm9yIHR3byBwdXJwb3NlczoKCjEuICBTZWVpbmcgdGhhdCB0aGUgdGhyZWUgdGlzc3VlIHR5cGVzIGFyZSBpbmRlZWQgZGlmZmVyZW50LgoyLiAgU2V0dGluZyB1cCB0aGUgdGFibGUgb2YgcmVzdWx0cyB3aXRoIGFwcHJvcHJpYXRlIHJvd3MvY29sdW1ucyBvZiAocm93cylnZW5lcwogICAgYW5kIChjb2x1bW5zKSBhbm5vdGF0aW9ucy4gIFdlIHdpbGwgdGhlcmVmb3JlIGFkZCB0byB0aGVzZSB0YWJsZXMgdGhlCiAgICByZXN1bHRzIG9mIHRoZSBleHByZXNzaW9uIGFuYWx5c2VzIHRoYXQgeW91IGFjdHVhbGx5IGRvIGNhcmUgYWJvdXQuCgpXaGVuIHdlIHJlY2VpdmUgZnVsbCByZXBsaWNhdGUgc2V0cywgdGhpcyBjaGVhdGVyIG1ldGhvZCBvZiBlbmNhcHN1bGF0aW5nIHRoZQpkYXRhIHdpbGwgbm90IGxvbmdlciBiZSByZXF1aXJlZC4KCiMjIyBXaXRoIHNhbG1vbgoKYGBge3IgZGVfc2EsIGZpZy5zaG93PSJoaWRlIn0KbW1fc2EgPC0gc2V0X2V4cHRfY29uZGl0aW9ucyhtbTM4X3NhbG1vbiwgZmFjdD0iY2VsbHR5cGUiKQptbV9ub3JtX3NhIDwtIHNtKG5vcm1hbGl6ZV9leHB0KG1tX3NhLCBjb252ZXJ0PSJycGttIiwgdHJhbnNmb3JtPSJsb2cyIiwgY29sdW1uPSJjZHNfbGVuZ3RoIikpCnBsb3RfcGNhKG1tX25vcm1fc2EpJHBsb3QKCm1tX2RlX3NhIDwtIGFsbF9wYWlyd2lzZShtbV9zYSwgbW9kZWxfYmF0Y2g9RkFMU0UpCmBgYAoKIyMjIFdpdGggaGlzYXQyCgpgYGB7ciBkZV9oaSwgZmlnLnNob3c9ImhpZGUifQptbV9oaSA8LSBzZXRfZXhwdF9jb25kaXRpb25zKG1tMzhfaGlzYXQsIGZhY3Q9ImNlbGx0eXBlIikKbW1fbm9ybV9oaSA8LSBzbShub3JtYWxpemVfZXhwdChtbV9oaSwgY29udmVydD0icnBrbSIsIHRyYW5zZm9ybT0ibG9nMiIsIGNvbHVtbj0iY2RzX2xlbmd0aCIpKQpwbG90X3BjYShtbV9ub3JtX2hpKSRwbG90CgptbV9kZV9oaSA8LSBzbShhbGxfcGFpcndpc2UobW1faGksIG1vZGVsX2JhdGNoPUZBTFNFKSkKYGBgCgojIyBTZXQgdXAgZm9yIGluaXRpYWwgYW5hbHlzaXMKClVudGlsIHdlIGdldCBmdWxsIHJlcGxpY2F0ZXMsIEkgd2lsbCBkbyBzaW1wbGUgc3VidHJhY3Rpb25zLgoKIyMjIFRlcm0gZGVmaW5pdGlvbgoKSW4gYW4gYXR0ZW1wdCB0byBrZWVwIHNvbWUgY2xhcml0eSBpbiB0aGUgdGVybXMgdXNlZCwgSSB3YW50IHRvIGRlZmluZSB0aGVtCm5vdy4gIFRoZXJlIGFyZSB0aHJlZSBjb250ZXh0cyBpbiB3aGljaCB3ZSB3aWxsIGNvbnNpZGVyIHRoZSBkYXRhOgoKMS4gIFRoZSBpbmRpdmlkdWFsIHNhbXBsZSB0eXBlLiAgV2hlbiBjb25zaWRlcmluZyBpbmRpdmlkdWFsIHNhbXBsZXMsIEkgd2lsbCB1c2UKICAgIHRocmVlIHRlcm1zIGluIHRoaXMgYW5kIG9ubHkgdGhpcyBjb250ZXh0OiB3aWxkLXR5cGUgKHd0KSwgaGV0LCBhbmQgbXV0LgoKMi4gIFRoZSBpbmRpdmlkdWFsIHRyYW5zbGF0b21lLiAgVGhlc2UgYXJlIGRlZmluZXMgYXMgc29tZXRoaW5nIC8gYmFzZWxpbmUuICBJCiAgICB3aWxsIGV4Y2x1c2l2ZWx5IGNhbGwgdGhlIHd0IHNhbXBsZXMgJ2Jhc2VsaW5lJyB3aGVuIHNwZWFraW5nIGluIHRoaXMKICAgIGNvbnRleHQuICBJIHdpbGwgZXhjbHVzaXZlbHkgc3RhdGUgJ25vcm1hbCcgd2hlbiByZWZlcnJpbmcgdG8gaGV0IC8gd3QKICAgIHNhbXBsZXMsIGFuZCBJIHdpbGwgc3RhdGUgJ2tvJyB3aGVuIHJlZmVycmluZyB0byBtdXQgLyB3dCBzYW1wbGVzIGluIHRoZQogICAgdHJhbnNsYXRvbWUgY29udGV4dC4KCjMuICBUcmFuc2xhdG9tZSB2cy4gdHJhbnNsYXRvbWUuICBXaGVuZXZlciBjb21wYXJpbmcgdHJhbnNsYXRvbWVzLCBJIHdpbGwgdXNlCiAgICB0aGUgbmFtZXMgYXMgaW4gIzIgYW5kIGFsd2F5cyBwdXQgdGhlIG51bWVyYXRvciBmaXJzdCB3aGVuIHdyaXRpbmcgdGhlIG5hbWUKICAgIG9mIGEgY29tcGFyaXNvbi4KClRoZSBtb3N0IGNvbXBsZXggZXhhbXBsZSBvZiB0aGUgYWJvdmUgbm9tZW5jbGF0dXJlIGlzOgoKIm5vcm1rb19yZXRkbGduIGlzIGRlZmluZWQgYXMgbm9ybXJldF92c19ub3JtZGxnbiAtIGtvcmV0X3ZzX2tvZGxnbiIKClRoaXMgc3RhdGVzIHdlIGFyZSBleGFtaW5pbmcgYXQgdGhlIHRyYW5zbGF0b21lIGNvbnRleHQ6CiAgIChub3JtKHJldGluYSB0cmFuc2xhdG9tZSkgLSBub3JtKGRsZ24gdHJhbnNsYXRvbWUpKSAtCiAgIChrbyhyZXRpbmEgdHJhbnNsYXRvbWUpIC0ga28oZGxnbiB0cmFuc2xhdG9tZSkpCgpXaGljaCBpbiB0dXJuIGlzIHN5bm9ueW1vdXMgdG8gdGhlIGZvbGxvd2luZyBhdCB0aGUgc2FtcGxlIGNvbnRleHQ6CiAgKChyZXRoZXQgLSByZXR3dCkgIC0gIChkbGduaGV0IC0gZGxnbnd0KSkgIC0KICAoKHJldGtvIC0gcmV0d3QpICAtICAoZGxnbmtvIC0gZGxnbnd0KSkKCk5vdyBsZXQgdXMgYXNzb2NpYXRlIHRoZSB2YXJpb3VzIHZhcmlhYmxlIG5hbWVzIHdpdGggdGhlIGFwcHJvcHJpYXRlIHNhbXBsZXM6CgpgYGB7ciBzYW1wbGVfbmFtZXN9CmRsZ253dCA8LSAiaXByZ2NfMDEiCnJldHd0IDwtICJpcHJnY18wMiIKc2Nud3QgPC0gImlwcmdjXzAzIgoKZGxnbmhldCA8LSAiaXByZ2NfMDQiCnJldGhldCA8LSAiaXByZ2NfMDUiCnNjbmhldCA8LSBOVUxMICAjIyBEb2VzIG5vdCB5ZXQgZXhpc3QuCgpkbGdubXV0IDwtICJpcHJnY18wNiIKcmV0bXV0IDwtICJpcHJnY18wNyIKc2NubXV0IDwtICJpcHJnY18wOCIKYGBgCgpHaXZlIHRoZXNlIHZhcmlhYmxlIG5hbWVzLCBub3cgbGV0cyBhc3NvY2lhdGUgY29sdW1ucyBvZiB0aGUgZXhwcmVzc2lvbiBkYXRhCndpdGggdGhlbS4gIFRoZXNlIGFyZSBhdCB0aGUgc2FtcGxlIGNvbnRleHQsIHNvIHRoZSBhcHByb3ByaWF0ZSBuYW1lcyBhcmU6Cid3dCcsICdoZXQnLCBhbmQgJ211dCcuICBJbiBlYWNoIGNhc2UgSSB3aWxsIHByZWZpeCB0aGUgZ2Vub3R5cGUgd2l0aCB0aGUgdGlzc3VlCnR5cGU6ICdyZXQnLCAnZGxnbicsIGFuZCAnc2NuJy4gIFRodXMgJ3JldHd0JyByZWZlcnMgdG8gdGhlIHNhbXBsZSB1c2VkCnRvIGNhbGN1bGF0ZSB0aGUgdHJhbnNsYXRvbWUgcmV0aW5hIGJhc2VsaW5lOyBpbiBjb250cmFzdCAnZGxnbm11dCcgaXMgdGhlCnNhbXBsZSB3aGljaCBwcm92aWRlcyB0aGUgZGxnbiBrbm9ja291dC4KCmBgYHtyIHNhbXBsZV9jb2x1bW5zfQojIyBTYW1wbGUgY29udGV4dAptbTM4X25vcm0gPC0gbW1fbm9ybV9zYQpkbGdud3QgPC0gZXhwcnMobW0zOF9ub3JtKVssIGRsZ253dF0KcmV0d3QgPC0gZXhwcnMobW0zOF9ub3JtKVssIHJldHd0XQpzY253dCA8LSBleHBycyhtbTM4X25vcm0pWywgc2Nud3RdCmRsZ25oZXQgPC0gZXhwcnMobW0zOF9ub3JtKVssIGRsZ25oZXRdCnJldGhldCA8LSBleHBycyhtbTM4X25vcm0pWywgcmV0aGV0XQpkbGdubXV0IDwtIGV4cHJzKG1tMzhfbm9ybSlbLCBkbGdubXV0XQpyZXRtdXQgPC0gZXhwcnMobW0zOF9ub3JtKVssIHJldG11dF0Kc2NubXV0IDwtIGV4cHJzKG1tMzhfbm9ybSlbLCBzY25tdXRdCmBgYAoKRWFjaCBvZiB0aGUgYWJvdmUgOCB2YXJpYWJsZXMgcHJvdmlkZXMgMSBjb2x1bW4gb2YgaW5mb3JtYXRpb24uIFdlIGhhdmUgMwpiYXNlbGluZSBjb21wYXJpc29ucyBhdmFpbGFibGUgdG8gdXMuICBJbiBlYWNoIG9mIHRoZXNlIHdlIGNvbXBhcmUgb25lIHd0CnNhbXBsZSB0byBhbm90aGVyLgoKYGBge3IgYmFzZWxpbmVfY29tcGFyaXNvbnN9CiMjIEJhc2VsaW5lIGNvbXBhcmlzb25zCnd0X2RsZ25yZXQgPC0gZGxnbnd0IC0gcmV0d3QKd3Rfc2NucmV0IDwtIHNjbnd0IC0gcmV0d3QKd3RfZGxnbnNjbiA8LSBkbGdud3QgLSBzY253dApgYGAKClNpbXVsdGFuZW91c2x5LCB3ZSBoYXZlIDUgYXZhaWxhYmxlIHRyYW5zbGF0b21lcy4gIFRoaXMgYXJlIHByb3ZpZGVkIGJ5CmNvbXBhcmluZyBlYWNoIGhldCBvciBtdXQgdG8gdGhlIGFzc29jaWF0ZWQgd3QuICBUaGVzZSB3aWxsIHRoZXJlZm9yZSByZWNlaXZlCm5hbWVzOiAnbm9ybScgYW5kICdrbycgaW5zdGVhZCBvZiAnaGV0JyBhbmQgJ211dCcuCgpgYGB7ciB0cmFuc2xhdG9tZXN9CiMjIFRyYW5zbGF0b21lIGNvbnRleHQKbm9ybXJldCA8LSByZXRoZXQgLSByZXR3dAprb3JldCA8LSByZXRtdXQgLSByZXR3dAprb3NjbiA8LSBzY25tdXQgLSBzY253dApub3JtZGxnbiA8LSBkbGduaGV0IC0gZGxnbnd0CmtvZGxnbiA8LSBkbGdubXV0IC0gZGxnbnd0CmBgYAoKR2l2ZW4gdGhlc2UgdHJhbnNsYXRvbWVzLCB0aGVyZSBhcmUgYSBmZXcgY29udHJhc3RzIG9mIGxpa2VseSBpbnRlcmVzdC4gIFRoZXNlCmFyZSBwZXJmb3JtZWQgYnkgY29tcGFyaW5nIHRoZSByZWxldmFudCB0cmFuc2xhdG9tZXMuCgpXaWxsIHdpbGwgc3BsaXQgdGhlc2UgaW50byA0IHNlcGFyYXRlIGNhdGVnb3JpZXM6CmhldCB2cyBoZXQsIGtvIHZzIGtvLCBrbyB2cyBoZXQsIGFuZCByYXRpbyB2cyByYXRpby4KCkZpbmFsbHksIG5vdGUgdGhhdCB3ZSBhcmUgYmVpbmcgZXhwbGljaXRseSByZWR1bmRhbnQgaW4gdGhlc2UgZGVmaW5pdGlvbnMuICBJIGFtCm1ha2luZyB2YXJpYWJsZSBuYW1lcyBmb3IgYm90aCB0aGUgYS9iIHJhdGlvIGFuZCB0aGUgYi9hIHJhdGlvLiAgVGh1cyB3ZSBoYXZlCnNvbWUgcmVkdW5kYW50bHkgcmVkdW5kYW50IChoYWhhKSBmbGV4aWJpbGl0eSB3aGVuIGRlY2lkaW5nIG9uIHdoYXQgd2Ugd2FudCB0byBwbG90LgoKYGBge3Igbm9ybV92c19ub3JtfQojIyBub3JtIHZzIG5vcm0Kbm9ybWRsZ25fdnNfbm9ybXJldCA8LSBub3JtZGxnbiAtIG5vcm1yZXQKbm9ybXJldF92c19ub3JtZGxnbiA8LSBub3JtcmV0IC0gbm9ybWRsZ24KYGBgCgpgYGB7ciBrb192c19rb30KIyMga28gdnMga28Ka29yZXRfdnNfa29kbGduIDwtIGtvcmV0IC0ga29kbGduCmtvZGxnbl92c19rb3JldCA8LSBrb2RsZ24gLSBrb3JldAoKa29yZXRfdnNfa29zY24gPC0ga29yZXQgLSBrb3Njbgprb3Njbl92c19rb3JldCA8LSBrb3NjbiAtIGtvcmV0Cgprb2RsZ25fdnNfa29zY24gPC0ga29kbGduIC0ga29zY24Ka29zY25fdnNfa29kbGduIDwtIGtvc2NuIC0ga29kbGduCmBgYAoKT24gdGhlIG90aGVyIGhhbmQsIEkgYW0gYXNzdW1pbmcgd2UgYWx3YXlzIHdhbnQgdGhlIG5vcm1hbHMgYXMgZGVub21pbmF0b3JzIGFuZAprb3MgYXMgbnVtZXJhdG9ycy4KCmBgYHtyIGtvX3ZzX25vcm19CiMjIGtvIHZzIG5vcm0Ka29yZXRfdnNfbm9ybXJldCA8LSBrb3JldCAtIG5vcm1yZXQKCmtvZGxnbl92c19ub3JtZGxnbiA8LSBrb2RsZ24gLSBub3JtZGxnbgpgYGAKCkZpbmFsbHksIGhlcmUgaXMgdGhlIHJhdGlvIG9mIHJhdGlvcyBleGFtcGxlIEkgcHJpbnRlZCBhYm92ZToKCkkgbmFtZWQgaXQgJ25vcm1rb19yZXRkbGduJyBpbiBhbiBhdHRlbXB0IHRvIG1ha2UgY2xlYXIgdGhhdCBpdCBpcyBhY3R1YWxseToKIChub3JtcmV0L25vcm1kbGduKS8oa29yZXQva29kbGduKQoKb3Igc3RhdGVkIGRpZmZlcmVudGx5OiAibm9ybSBkaXZpZGVkIGJ5IGtvIGZvciByZXQgZGl2aWRlZCBieSBkbGduLiIKCmBgYHtyIHJvcn0KIyMgcmF0aW8gb2YgcmF0aW9zCm5vcm1rb19yZXRkbGduIDwtIG5vcm1yZXRfdnNfbm9ybWRsZ24gLSBrb3JldF92c19rb2RsZ24KYGBgCgojIyBEZWZpbmUgYSBtYXRyaXggb2YgdGhlc2UgdmFsdWVzLgoKTXkgbWF0cml4IG9mIGRhdGEgd2lsbCBub3cgY29udGFpbiAxIGNvbHVtbiBmb3IgZWFjaCBvZiB0aGUgYWJvdmUgMjcKc2FtcGxlcy9jb21wYXJpc29ucy4KCmBgYHtyIG1hdHJpeF9vZl92YWx1ZXN9CnBhaXJfbXRyeCA8LSBjYmluZCgKICAjIyBJbmRpdmlkdWFsIHNhbXBsZXMKICBkbGdud3QsIHJldHd0LCBzY253dCwgZGxnbmhldCwgcmV0aGV0LCBkbGdubXV0LCByZXRtdXQsIHNjbm11dCwKICAjIyBCYXNlbGluZSBjb21wYXJpc29ucwogIHd0X2RsZ25yZXQsIHd0X3NjbnJldCwgd3RfZGxnbnNjbiwKICAjIyBCYXNlbGluZSBzdWJ0cmFjdGlvbnMKICBub3JtZGxnbiwgbm9ybXJldCwga29kbGduLCBrb3JldCwga29zY24sCiAgIyMgaGV0X3ZzX2hldCwgb2Ygd2hpY2ggdGhlcmUgaXMgb25seSAxIGJlY2F1c2Ugd2UgZG8gbm90IGhhdmUgaGV0c2NuCiAgbm9ybWRsZ25fdnNfbm9ybXJldCwgbm9ybXJldF92c19ub3JtZGxnbiwKICAjIyBrb192c19rbywgb2Ygd2hpY2ggd2UgaGF2ZSAzCiAga29yZXRfdnNfa29kbGduLCBrb2RsZ25fdnNfa29yZXQsCiAga29yZXRfdnNfa29zY24sIGtvc2NuX3ZzX2tvcmV0LAogIGtvZGxnbl92c19rb3Njbiwga29zY25fdnNfa29kbGduLAogICMjIGtvX3ZzX2hldCwgMyBpbmNsdWRpbmcgb25lIGdldHRpbmcgYXJvdW5kIG1pc3NpbmcgaGV0c2NuCiAga29yZXRfdnNfbm9ybXJldCwga29kbGduX3ZzX25vcm1kbGduLAogICMjIHJhdGlvIG9mIHJhdGlvcwogIG5vcm1rb19yZXRkbGduKQpgYGAKCiMjIEN1dG9mZnMKCkkgYW0gbm90IHN1cmUgaWYgd2Ugd2lsbCB1c2UgdGhlc2UgaW5kZXhlcywgYnV0IEkgYW0gd3JpdGluZyB0aGVzZSBvdXQgYXMKc3Vic2V0cyBvZiBnZW5lcyB0byBsb29rIGF0LiAgVGhlc2UgaW5kZXhlcyBhcmUgc3RhdGluZyB0aGF0LCBnaXZlbiBhIGN1dG9mZgooMCksIHdlIHdhbnQgdG8gbG9vayBhdCBvbmx5IHRoZSBnZW5lcyB3aGljaCBoYXZlIGhpZ2hlciB4IC8gYmFzZWxpbmUgdmFsdWVzCnRoYW4gdGhlIGN1dG9mZi4KCgpgYGB7ciBjdXRvZmZzfQojIyBRdWVyaWVzIGFib3V0IGdlbmUgc3Vic2V0cy4KIyMgVGhlc2UgYXJlIGFsbCBpbiB0aGUgY29udGV4dCBvZiB0cmFuc2xhdG9tZXMuCmN1dG9mZiA8LSAwCnJldF9rZXB0X2lkeCA8LSBub3JtcmV0ID4gY3V0b2ZmICYga29yZXQgPiBjdXRvZmYKc2NuX2tlcHRfaWR4IDwtIGtvc2NuID4gY3V0b2ZmCmRsZ25fa2VwdF9pZHggPC0gbm9ybWRsZ24gPiBjdXRvZmYgJiBrb2RsZ24gPiBjdXRvZmYKcmV0X2RsZ25fa2VwdF9pZHggPC0gcmV0X2tlcHRfaWR4ICYgZGxnbl9rZXB0X2lkeApyZXRfc2NuX2tlcHRfaWR4IDwtIHJldF9rZXB0X2lkeCAmIHNjbl9rZXB0X2lkeApkbGduX3Njbl9rZXB0X2lkeCA8LSBkbGduX2tlcHRfaWR4ICYgc2NuX2tlcHRfaWR4CgojI25vcm1kbGduX3ZzX25vcm1yZXRbIXJldF9kbGduX2tlcHRfaWR4XSA8LSBOQQojI25vcm1yZXRfdnNfbm9ybWRsZ25bIXJldF9kbGduX2tlcHRfaWR4XSA8LSBOQQojI2tvcmV0X3ZzX2tvZGxnblshcmV0X2RsZ25fa2VwdF9pZHhdIDwtIE5BCiMja29kbGduX3ZzX2tvcmV0WyFyZXRfZGxnbl9rZXB0X2lkeF0gPC0gTkEKIyNrb3JldF92c19rb3NjblshcmV0X3Njbl9rZXB0X2lkeF0gPC0gTkEKIyNrb3Njbl92c19rb3JldFshcmV0X3Njbl9rZXB0X2lkeF0gPC0gTkEKIyNrb2RsZ25fdnNfa29zY25bIWRsZ25fc2NuX2tlcHRfaWR4XSA8LSBOQQojI2tvc2NuX3ZzX2tvZGxnblshZGxnbl9zY25fa2VwdF9pZHhdIDwtIE5BCiMja29yZXRfdnNfbm9ybXJldFshcmV0X2tlcHRfaWR4XSA8LSBOQQojI2tvZGxnbl92c19ub3JtZGxnblshZGxnbl9rZXB0X2lkeF0gPC0gTkEKIyNub3Jta29fcmV0ZGxnbiA8LSBub3Jta29fcmV0ZGxnblshcmV0X2RsZ25fa2VwdF9pZHhdIDwtIE5BCmBgYAoKIyMgQWRkIHRoZSBtYXRyaXggdG8gdGhlIGRpZmZlcmVudGlhbCBleHByZXNzaW9uCgpJIHdpbGwgdXNlIG15IGZ1bmN0aW9uIGNvbWJpbmVfZGVfdGFibGVzKCkgdG8gYWRkIHRoaXMgaW5mb3JtYXRpb24gdG8gbXkKZXhpc3RpbmcgYW5ub3RhdGlvbiBkYXRhIGFsb25nIHdpdGggdGhlIHJlc3VsdHMgZnJvbSB0aGUgc3RhdGlzdGljYWxseSB2YWxpZApjb21wYXJpc29uIG9mIHRoZSB0aHJlZSB0aXNzdWUgdHlwZXMuCgpgYGB7ciBhZGRfbWF0cml4X2RlfQptbV90YWJsZXMgPC0gc20oY29tYmluZV9kZV90YWJsZXMoCiAgbW1fZGVfc2EsIGV4dHJhX2Fubm90PXBhaXJfbXRyeCwKICBleGNlbD1nbHVlOjpnbHVlKCJleGNlbC97cnVuZGF0ZX1tbV9zYWxtb25fdGFibGVzLXZ7dmVyfS54bHN4IikpKQpgYGAKCiMgUGxvdHMgb2YgaW50ZXJlc3RpbmcgY29tcGFyaXNvbnMKCmBgYHtyIHNvbWVfcGxvdHN9CiMjIFB1dCByZXRpbmEgYmFzZWxpbmUgb24geSBheGlzIGFzIGJsYWNrLCByZXRpbmEgaGV0IG9uIHggYXhpcyBhcyBibGFjay4KIyMgVGhlbiByZWNvbG9yIGEgc3Vic2V0IG9mIHRoZXNlIGFzIHJlZCwgdGhlIHJlZHMgYXJlIHdoZW4gbm9ybXJldCA+IDAKbGlicmFyeShnZ3Bsb3QyKQpwbG90dGVkIDwtIGFzLmRhdGEuZnJhbWUocGFpcl9tdHJ4WywgYygicmV0aGV0IiwgInJldHd0IildKQpyZWRfaWR4IDwtIG5vcm1yZXQgPiAwCnBsb3R0ZWRbLCAiY29sb3IiXSA8LSBpZmVsc2UocmVkX2lkeCwgImJsYWNrIiwgInJlZCIpCnJldF9oZXR3dCA8LSBnZ3Bsb3QyOjpnZ3Bsb3QocGxvdHRlZCwgYWVzX3N0cmluZyh4PSJyZXRoZXQiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgeT0icmV0d3QiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29sb3I9ImNvbG9yIikpICsKICBnZW9tX3BvaW50KGFscGhhPTAuNSkgKwogIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXM9YygiYmxhY2siLCAicmVkIikpCnJldF9oZXR3dAoKcGxvdHRlZCA8LSBwYWlyX210cnhbLCBjKCJyZXRtdXQiLCAicmV0d3QiKV0KcmV0X211dHd0IDwtIHBsb3Rfc2NhdHRlcihwbG90dGVkKQpyZXRfbXV0d3QKCnBsb3R0ZWQgPC0gcGFpcl9tdHJ4WywgYygiZGxnbmhldCIsICJkbGdud3QiKV0KZGxnbl9oZXR3dCA8LSBwbG90X3NjYXR0ZXIocGxvdHRlZCkKZGxnbl9oZXR3dApwbG90dGVkIDwtIHBhaXJfbXRyeFssIGMoImRsZ25tdXQiLCAiZGxnbnd0IildCmRsZ25fbXV0d3QgPC0gcGxvdF9zY2F0dGVyKHBsb3R0ZWQpCmRsZ25fbXV0d3QKCnBsb3R0ZWQgPC0gcGFpcl9tdHJ4WywgYygic2NubXV0IiwgInNjbnd0IildCnNjbl9tdXR3dCA8LSBwbG90X3NjYXR0ZXIocGxvdHRlZCkKc2NuX211dHd0CgojIyBBeG9uIHRyYW5zbGF0b21lIHNwZWNpZmljCiMjICB4LWF4aXM6IG5vcm1kbGduX3ZzX25vcm1yZXQgb3Igbm9ybXJldF92c19ub3JtZGxnbiwKIyMgICAgICAgICAgICAgIF5eXl4KIyMgIHktYXhpczogZGxnbnd0LXJldHd0IChiYXNlbGluZSBkbGduIC0gYmFzZWxpbmUgcmV0aW5hKQpwbG90dGVkIDwtIGFzLmRhdGEuZnJhbWUocGFpcl9tdHJ4WywgYygibm9ybWRsZ25fdnNfbm9ybXJldCIsICJ3dF9kbGducmV0IildKQpyZWRfaWR4IDwtIG5vcm1yZXQgPiAwCnBsb3R0ZWRbLCAiY29sb3IiXSA8LSBpZmVsc2UocmVkX2lkeCwgImJsYWNrIiwgInJlZCIpCmF4b25fdHJhbnNfcmV0X3RhcmdldCA8LSBnZ3Bsb3QyOjpnZ3Bsb3QocGxvdHRlZCwgYWVzX3N0cmluZyh4PSJub3JtZGxnbl92c19ub3JtcmV0IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHk9Ind0X2RsZ25yZXQiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29sb3I9ImNvbG9yIikpICsKICBnZW9tX3BvaW50KGFscGhhPTAuNSkgKwogIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXM9YygiYmxhY2siLCAicmVkIikpCmF4b25fdHJhbnNfcmV0X3RhcmdldAoKIyMgRExHTiB0cmFuc2xhdG9tZSB3cnQuIFJldGluYSB0cmFuc2xhdG9tZQpwbG90dGVkIDwtIHBhaXJfbXRyeFssIGMoIm5vcm1yZXQiLCAibm9ybWRsZ24iKV0Kbm9ybXJldF9ub3JtZGxnbiA8LSBwbG90X3NjYXR0ZXIocGxvdHRlZCkKbm9ybXJldF9ub3JtZGxnbgoKcGxvdHRlZCA8LSBwYWlyX210cnhbLCBjKCJrb3JldCIsICJrb2RsZ24iKV0Ka29yZXRfa29kbGduIDwtIHBsb3Rfc2NhdHRlcihwbG90dGVkKQprb3JldF9rb2RsZ24KCnBsb3R0ZWQgPC0gcGFpcl9tdHJ4WywgYygia29yZXQiLCAia29zY24iKV0Ka29yZXRfa29zY25fcGxvdCA8LSBwbG90X3NjYXR0ZXIocGxvdHRlZCkKa29yZXRfa29zY25fcGxvdAoKcGxvdHRlZCA8LSBwYWlyX210cnhbLCBjKCJub3JtZGxnbiIsICJrb2RsZ24iKV0Kbm9ybWRsZ25fa29kbGduX3Bsb3QgPC0gcGxvdF9zY2F0dGVyKHBsb3R0ZWQpCm5vcm1kbGduX2tvZGxnbl9wbG90CgpwbG90dGVkIDwtIHBhaXJfbXRyeFssIGMoIm5vcm1yZXQiLCAia29yZXQiKV0Kbm9ybXJldF9rb3JldF9wbG90IDwtIHBsb3Rfc2NhdHRlcihwbG90dGVkKQpub3JtcmV0X2tvcmV0X3Bsb3QKCnBsb3R0ZWQgPC0gcGFpcl9tdHJ4WywgYygibm9ybXJldF92c19ub3JtZGxnbiIsICJrb3JldF92c19rb2RsZ24iKV0Kbm9ybWFsX2tvX2F4b25fdHJhbnNsYXRvbWUgPC0gcGxvdF9zY2F0dGVyKHBsb3R0ZWQpCm5vcm1hbF9rb19heG9uX3RyYW5zbGF0b21lCmBgYAoKIyBTb21lIHBpY3R1cmVzCgpBcyBJIHVuZGVyc3RhbmQgaXQsIHRoZXJlIGlzIHNvbWUgaW50ZXJlc3QgaW4gYW4gb250b2xvZ3kgc2VhcmNoIHVzaW5nIHRoZSByYXRpbyBvZiByYXRpb3MuCgpgYGB7ciBvdGhlcl9jb250cmFzdHN9CnJvciA8LSBub3Jta29fcmV0ZGxnbgp1cF9pZHggPC0gcm9yID49IDEKZG93bl9pZHggPC0gcm9yIDw9IC0xCnJvcl91cCA8LSByb3JbdXBfaWR4XQpsZW5ndGgocm9yX3VwKQpyb3JfZG93biA8LSByb3JbZG93bl9pZHhdCmxlbmd0aChyb3JfZG93bikKCnJvcl9ncHJvZmlsZXJfdXAgPC0gc2ltcGxlX2dwcm9maWxlcihzaWdfZ2VuZXM9cm9yX3VwLCBzcGVjaWVzPSJtbXVzY3VsdXMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZXhjZWw9Z2x1ZTo6Z2x1ZSgiZXhjZWwve3J1bmRhdGV9bW1fcm9yX2dwZm9pbGVyX3VwLXZ7dmVyfS54bHN4IikpCnJvcl9ncHJvZmlsZXJfdXAkcHZhbHVlX3Bsb3RzJG1mcF9wbG90X292ZXIKcm9yX2dwcm9maWxlcl91cCRwdmFsdWVfcGxvdHMkYnBwX3Bsb3Rfb3Zlcgpyb3JfZ3Byb2ZpbGVyX3VwJHB2YWx1ZV9wbG90cyRjY3BfcGxvdF9vdmVyCnJvcl9ncHJvZmlsZXJfdXAkcHZhbHVlX3Bsb3RzJHRmX3Bsb3Rfb3Zlcgpyb3JfZ3Byb2ZpbGVyX3VwJHB2YWx1ZV9wbG90cyRocF9wbG90X292ZXIKCnJvcl9ncHJvZmlsZXJfZG93biA8LSBzaW1wbGVfZ3Byb2ZpbGVyKHNpZ19nZW5lcz1yb3JfZG93biwgc3BlY2llcz0ibW11c2N1bHVzIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZXhjZWw9Z2x1ZTo6Z2x1ZSgiZXhjZWwve3J1bmRhdGV9bW1fcm9yX2dwZm9pbGVyX2Rvd24tdnt2ZXJ9Lnhsc3giKSkKcm9yX2dwcm9maWxlcl9kb3duJHB2YWx1ZV9wbG90cyRtZnBfcGxvdF9vdmVyCnJvcl9ncHJvZmlsZXJfZG93biRwdmFsdWVfcGxvdHMkYnBwX3Bsb3Rfb3Zlcgpyb3JfZ3Byb2ZpbGVyX2Rvd24kcHZhbHVlX3Bsb3RzJHJlYWN0b21lX3Bsb3Rfb3Zlcgpyb3JfZ3Byb2ZpbGVyX2Rvd24kcHZhbHVlX3Bsb3RzJGNjcF9wbG90X292ZXIKcm9yX2dwcm9maWxlcl9kb3duJHB2YWx1ZV9wbG90cyR0Zl9wbG90X292ZXIKYGBgCgpgYGB7ciBzYXZlbWUsIGV2YWw9RkFMU0V9CnBhbmRlcjo6cGFuZGVyKHNlc3Npb25JbmZvKCkpCm1lc3NhZ2UocGFzdGUwKCJUaGlzIGlzIGhwZ2x0b29scyBjb21taXQ6ICIsIGdldF9naXRfY29tbWl0KCkpKQp0aGlzX3NhdmUgPC0gcGFzdGUwKGdzdWIocGF0dGVybj0iXFwuUm1kIiwgcmVwbGFjZT0iIiwgeD1ybWRfZmlsZSksICItdiIsIHZlciwgIi5yZGEueHoiKQptZXNzYWdlKHBhc3RlMCgiU2F2aW5nIHRvICIsIHRoaXNfc2F2ZSkpCnRtcCA8LSBzbShzYXZlbWUoZmlsZW5hbWU9dGhpc19zYXZlKSkKYGBgCgpgYGB7ciBsb2FkbWUsIGV2YWw9RkFMU0V9CmxvYWRtZShmaWxlbmFtZT10aGlzX3NhdmUpCmBgYAo=