Before we begin, a couple of parameters which have given me grief.

## Used by the various functions which cross reference grange data
## The SEs used in this document are getting this from the orgdb
## which includes this information in multiple columns with different
## chromosome ID prefixes.  E.g. sometimes it is just 1,2,3, ... other times
## it is LpaL1, LpaL2, LpaL3, ...
exp_chr_col <- "sequence_id"
## The tritrypdb also puts the start/stop/strand information in multiple places
exp_start_col <- "coding_start"
exp_end_col <- "coding_end"

1 Introduction

This document will visualize the TMRC2 samples before completing the various differential expression and variant analyses in the hopes of getting an understanding of how the various samples relate to each other.

1.1 Initial library size

Start off with the library sizes of the original dataset. The main thing to note is that we have quite a large variance in coverage. A few of these samples are highly likely to be removed shortly (looking at you, TMRC20001 and TMRC20095)

libsizes <- plot_libsize(lp_se)
libsizes
## Library sizes of 92 samples, 
## ranging from 564,812 to 1.37e+08.

dev <- pp("images/lp_se_libsizes.png", width = 18, height = 9)
libsizes$plot
closed <- dev.off()

Library sizes of the protein coding gene counts observed per sample. The samples were mapped with the EuPathDB revision 36 of the Leishmania (Viannia) panamensis strain MHOM/COL/81L13 genome; the alignments were sorted, indexed, and counted via htseq using the gene features, and non-protein coding features were excluded. The per-sample sums of the remaining matrix were plotted to check that the relative sample coverage is sufficient and not too divergent across samples. Bars are colored according to strain/zymodeme annotation: red: zymodeme 2.3; blue: zymodeme 2.2; Leishmania braziliensis-like strains b2904, z1.0, and z1.5: purple; zymodemes which are most similar to 2.3, comprising z2.4 is light brown; zymodemes most similar to 2.2, comprising z3.0, z2.0, z2.1, and z3.2 are light gray, dark gray, dark brown, and gray respectively.

1.2 Non-zero genes with respect to coverage

This plot is usually our primary arbiter for sample removing based on coverage. We pick a semi-arbitrary cutoff based on both coverage and genes observed. In this instance 8,600 genes seems likely?

The cutoff argument prints out samples with gene coverage < that proportion. I think we already dropped in the sample sheet the most problematic samples, so it may not actually print anything.

## I think samples 7,10 should be removed at minimum, probably also 9,11
nonzero <- plot_nonzero(lp_se, cutoff = 0.7, y_intercept = 0.99)
## Scale for colour is already present.
## Adding another scale for colour, which will replace the existing scale.
## Scale for fill is already present.
## Adding another scale for fill, which will replace the existing scale.
## Warning: Using `size` aesthetic for lines was deprecated in ggplot2 3.4.0.
## i Please use `linewidth` instead.
## i The deprecated feature was likely used in the hpgltools package.
##   Please report the issue to the authors.
## This warning is displayed once per session.
## Call `lifecycle::last_lifecycle_warnings()` to see where this warning was
## generated.
nonzero
## A non-zero genes plot of 92 samples.
## These samples have an average 28.78 CPM coverage and 8694 genes observed, ranging from 8554 to
## 8749.

dev <- pp(file = "images/lp_nonzero.png", width = 9, height = 9)
nonzero$plot
closed <- dev.off()

Differences in relative gene content with respect to sequencing coverage. The per-sample number of observed genes was plotted with respect to the relative CPM coverage in order to check that the samples are sufficiently and similarly diverse. Many samples were observed near or at the putative asymptote of likely gene content; no samples were observed with fewer than 65% of the Leishmania panamensis genes included. Note that the range of genes observed is quite small, 8500 <= x < 8700 genes, however this was plotted after already excluding samples with fewer than 8500 genes observed (of which there were 2) and any samples with fewer than 5 million protein coding mapped reads (there were 2 samples that had more than 8500 genes observed in less than 5 million reads).

lp_box <- plot_boxplot(lp_se)
## 7722 entries are 0.  We are on a log scale, adding 1 to the data.
##          TMRC20001 TMRC20065 TMRC20004 TMRC20005 TMRC20066 TMRC20039 TMRC20037
## min         0.0000    0.0000    0.0000    0.0000    0.0000    0.0000    0.0000
## q1          8.1254    9.5527    4.6439    9.1137    9.0526    9.6221    9.4310
## median      8.9366   10.5196    5.4919    9.9263    9.9571   10.4974   10.2621
## mean        8.6262   10.2353    5.2930    9.6450    9.6716   10.2874   10.0346
## q3          9.6439   11.3971    6.2095   10.6463   10.7624   11.3536   11.0708
## max        18.2820   19.4189   13.8030   17.8164   18.0472   17.9602   18.3070
## iqr         1.5184    1.8445    1.5656    1.5326    1.7098    1.7315    1.6398
## iqr_high   11.9215   14.1638    8.5578   12.9452   13.3271   13.9508   13.5305
## iqr_low    -2.2777   -2.7667   -2.3484   -2.2989   -2.5647   -2.5973   -2.4597
## sd          1.8938    2.0949    1.5637    1.9216    1.9473    1.9117    1.9486
## var         3.5865    4.3885    2.4450    3.6926    3.7918    3.6544    3.7969
## stdvar      0.4158    0.4288    0.4619    0.3828    0.3921    0.3552    0.3784
##          TMRC20038 TMRC20067 TMRC20068 TMRC20041 TMRC20015 TMRC20009 TMRC20010
## min         0.0000    0.0000    0.0000    0.0000    0.0000    0.0000    0.0000
## q1          9.5469    9.9622    9.7125    9.9073   11.8918   11.2680   10.2378
## median     10.3993   10.7969   10.6366   10.8576   12.7526   12.1232   11.1370
## mean       10.1845   10.4659   10.3475   10.6827   12.4476   11.8678   10.8502
## q3         11.2324   11.4903   11.4355   11.7842   13.5341   12.9119   11.9354
## max        19.1131   19.4121   18.9980   18.0636   21.9990   20.9345   21.4671
## iqr         1.6855    1.5282    1.7230    1.8770    1.6423    1.6439    1.6976
## iqr_high   13.7607   13.7826   14.0201   14.5997   15.9976   15.3778   14.4819
## iqr_low    -2.5283   -2.2923   -2.5845   -2.8154   -2.4635   -2.4659   -2.5465
## sd          1.9580    1.9433    1.9994    1.9266    2.0988    1.9742    2.0191
## var         3.8336    3.7762    3.9978    3.7116    4.4050    3.8974    4.0767
## stdvar      0.3764    0.3608    0.3864    0.3474    0.3539    0.3284    0.3757
##          TMRC20016 TMRC20011 TMRC20012 TMRC20013 TMRC20017 TMRC20014 TMRC20018
## min         0.0000    0.0000    0.0000    0.0000    0.0000     0.000    0.0000
## q1         10.6475   10.0701   10.1774   10.6268   11.3333    10.905   10.9507
## median     11.5379   10.9270   11.0317   11.5226   12.1957    11.758   11.8498
## mean       11.2610   10.6674   10.7377   11.2396   11.9434    11.504   11.5878
## q3         12.3424   11.7066   11.8222   12.3238   12.9921    12.542   12.6889
## max        20.5137   19.4904   19.7846   20.4443   20.9297    20.307   20.7357
## iqr         1.6950    1.6365    1.6448    1.6970    1.6588     1.636    1.7381
## iqr_high   14.8849   14.1613   14.2893   14.8693   15.4802    14.996   15.2960
## iqr_low    -2.5424   -2.4547   -2.4671   -2.5455   -2.4881    -2.455   -2.6072
## sd          2.0175    1.9424    2.0390    2.0383    1.9937     1.969    2.0402
## var         4.0702    3.7728    4.1574    4.1547    3.9750     3.876    4.1625
## stdvar      0.3614    0.3537    0.3872    0.3697    0.3328     0.337    0.3592
##          TMRC20019 TMRC20070 TMRC20020 TMRC20021 TMRC20022 TMRC20024 TMRC20036
## min         0.0000    0.0000    0.0000    0.0000    0.0000    0.0000    0.0000
## q1         12.1618   10.1589   12.4500   12.5341   10.9341   11.6718    9.0954
## median     13.0218   11.0678   13.3120   13.4197   11.8080   12.5395    9.9233
## mean       12.7333   10.7881   13.0315   13.1096   11.5668   12.3085    9.7180
## q3         13.7830   11.8624   14.0826   14.1917   12.5891   13.3716   10.7089
## max        21.3726   19.6638   21.4005   21.3018   20.4087   21.5897   18.5279
## iqr         1.6211    1.7035    1.6327    1.6576    1.6551    1.6998    1.6135
## iqr_high   16.2147   14.4177   16.5316   16.6781   15.0717   15.9213   13.1291
## iqr_low    -2.4317   -2.5553   -2.4490   -2.4864   -2.4826   -2.5497   -2.4202
## sd          1.9919    1.9723    1.9774    2.0605    1.8626    1.9635    1.8184
## var         3.9676    3.8900    3.9102    4.2456    3.4693    3.8555    3.3066
## stdvar      0.3116    0.3606    0.3001    0.3238    0.2999    0.3132    0.3403
##          TMRC20069 TMRC20033 TMRC20026 TMRC20031 TMRC20076 TMRC20073 TMRC20055
## min         0.0000    0.0000    0.0000    0.0000    0.0000    0.0000    0.0000
## q1         10.2204   11.8863   11.0191   10.8619    9.8906    9.8688    9.4579
## median     11.0765   12.7419   11.9187   11.7349   10.7570   10.8090   10.3038
## mean       10.8261   12.4892   11.6662   11.4706   10.5245   10.5587   10.0871
## q3         11.8619   13.5257   12.7317   12.5416   11.5961   11.6860   11.1253
## max        19.4667   21.7782   20.6152   20.5766   20.0927   19.9017   19.3504
## iqr         1.6415    1.6395    1.7127    1.6798    1.7054    1.8171    1.6674
## iqr_high   14.3241   15.9849   15.3008   15.0613   14.1542   14.4116   13.6263
## iqr_low    -2.4622   -2.4592   -2.5690   -2.5197   -2.5581   -2.7257   -2.5010
## sd          1.9064    1.9242    1.9417    1.9792    1.9567    2.0178    1.9361
## var         3.6345    3.7026    3.7703    3.9173    3.8285    4.0716    3.7483
## stdvar      0.3357    0.2965    0.3232    0.3415    0.3638    0.3856    0.3716
##          TMRC20079 TMRC20071 TMRC20078 TMRC20094 TMRC20042 TMRC20058 TMRC20072
## min         0.0000    0.0000    0.0000    0.0000    0.0000    0.0000    0.0000
## q1          9.7814    9.6041   10.1306    8.8009    9.7499   10.0324    9.8704
## median     10.6887   10.4625   10.9922    9.6671   10.6073   10.9054   10.7151
## mean       10.4475   10.1702   10.7160    9.4374   10.3742   10.6369   10.4508
## q3         11.5661   11.2490   11.7645   10.5196   11.4506   11.7201   11.4974
## max        20.0670   19.4627   19.7821   18.5989   19.3668   19.9893   19.4477
## iqr         1.7847    1.6449    1.6339    1.7187    1.7007    1.6877    1.6270
## iqr_high   14.2431   13.7163   14.2153   13.0977   14.0016   14.2518   13.9378
## iqr_low    -2.6770   -2.4673   -2.4508   -2.5781   -2.5510   -2.5316   -2.4405
## sd          2.0145    1.9832    1.9813    1.9366    1.9737    2.0418    1.9952
## var         4.0582    3.9330    3.9256    3.7503    3.8955    4.1688    3.9807
## stdvar      0.3884    0.3867    0.3663    0.3974    0.3755    0.3919    0.3809
##          TMRC20059 TMRC20048 TMRC20057 TMRC20088 TMRC20056 TMRC20060 TMRC20077
## min         0.0000     0.000    0.0000    0.0000    0.0000    0.0000    0.0000
## q1          9.8704     9.881    9.9218    8.9425    8.9366    9.9009    9.7781
## median     10.7789    10.749   10.7985    9.8090    9.8041   10.7649   10.6773
## mean       10.5042    10.499   10.5432    9.5452    9.5564   10.5338   10.4624
## q3         11.5929    11.554   11.5928   10.5668   10.6101   11.5688   11.5379
## max        20.1892    18.608   19.4695   18.3197   18.7969   19.0986   19.0149
## iqr         1.7226     1.672    1.6710    1.6243    1.6735    1.6679    1.7599
## iqr_high   14.1768    14.062   14.0993   13.0031   13.1203   14.0707   14.1778
## iqr_low    -2.5838    -2.509   -2.5064   -2.4364   -2.5102   -2.5019   -2.6398
## sd          2.0074     1.965    1.9102    1.8646    1.9198    1.9247    1.9483
## var         4.0295     3.863    3.6490    3.4768    3.6856    3.7044    3.7957
## stdvar      0.3836     0.368    0.3461    0.3642    0.3857    0.3517    0.3628
##          TMRC20074 TMRC20063 TMRC20053 TMRC20052 TMRC20064 TMRC20075 TMRC20051
## min         0.0000    0.0000    0.0000    0.0000    0.0000    0.0000    0.0000
## q1          9.4635    9.2644    9.3597    9.9890    9.4553    8.6582    9.7385
## median     10.3236   10.1749   10.2070   10.9425   10.3685    9.5868   10.5803
## mean       10.0886    9.9258    9.9956   10.6635   10.1149    9.3414   10.3294
## q3         11.1424   11.0005   11.0556   11.8037   11.2574   10.4336   11.3918
## max        17.8595   17.3060   19.3199   19.2045   18.5136   17.5756   19.3308
## iqr         1.6789    1.7361    1.6959    1.8147    1.8021    1.7754    1.6533
## iqr_high   13.6608   13.6047   13.5994   14.5258   13.9605   13.0966   13.8717
## iqr_low    -2.5184   -2.6041   -2.5438   -2.7220   -2.7031   -2.6631   -2.4799
## sd          1.9334    1.9257    1.9315    2.0696    2.0351    1.9886    2.0110
## var         3.7382    3.7083    3.7309    4.2833    4.1418    3.9545    4.0440
## stdvar      0.3705    0.3736    0.3733    0.4017    0.4095    0.4233    0.3915
##          TMRC20050 TMRC20049 TMRC20062 TMRC20110 TMRC20080 TMRC20043 TMRC20083
## min         0.0000    0.0000    0.0000    0.0000    0.0000    0.0000    0.0000
## q1          9.6165   10.3298    9.6817   10.6202    9.5038    9.9189    9.1599
## median     10.4367   11.1618   10.5236   11.4681   10.3750   10.7624   10.0028
## mean       10.1989   10.9229   10.2756   11.2152   10.1137   10.4876    9.7508
## q3         11.1699   11.9541   11.3368   12.2868   11.1971   11.5416   10.7682
## max        18.6003   19.8912   18.6611   21.2189   18.6962   19.8699   18.4172
## iqr         1.5534    1.6243    1.6551    1.6666    1.6932    1.6227    1.6083
## iqr_high   13.5000   14.3906   13.8194   14.7866   13.7369   13.9757   13.1807
## iqr_low    -2.3301   -2.4365   -2.4827   -2.4998   -2.5399   -2.4341   -2.4125
## sd          1.7854    1.9919    2.0013    2.0318    2.0201    1.9853    1.8741
## var         3.1877    3.9677    4.0054    4.1282    4.0807    3.9414    3.5123
## stdvar      0.3126    0.3632    0.3898    0.3681    0.4035    0.3758    0.3602
##          TMRC20054 TMRC20085 TMRC20046 TMRC20093 TMRC20089 TMRC20047 TMRC20090
## min          0.000    0.0000    0.0000    0.0000    0.0000    0.0000    0.0000
## q1           9.923    9.2408   10.0417    9.6600    9.3332    9.6546    9.3382
## median      10.796   10.1357   10.8463   10.5770   10.2574   10.4993   10.2252
## mean        10.523    9.8587   10.5675   10.3295   10.0049   10.2398    9.9736
## q3          11.614   10.9593   11.5558   11.4346   11.1296   11.2946   11.0632
## max         19.643   18.5656   18.9656   19.1285   19.0696   18.5807   18.7419
## iqr          1.691    1.7185    1.5141    1.7746    1.7964    1.6400    1.7250
## iqr_high    14.151   13.5370   13.8270   14.0966   13.8243   13.7546   13.6508
## iqr_low     -2.536   -2.5777   -2.2712   -2.6619   -2.6947   -2.4600   -2.5876
## sd           2.028    1.9953    1.9014    1.9962    2.0322    1.9705    2.0165
## var          4.114    3.9814    3.6154    3.9849    4.1300    3.8830    4.0661
## stdvar       0.391    0.4038    0.3421    0.3858    0.4128    0.3792    0.4077
##          TMRC20044 TMRC20045 TMRC20105 TMRC20108 TMRC20109 TMRC20098 TMRC20096
## min          0.000    0.0000    0.0000    0.0000    0.0000    0.0000    0.0000
## q1           9.984    9.7381    8.8887   10.3106   10.1602    9.4778    9.5680
## median      10.827   10.5765    9.7398   11.1630   10.9940   10.4019   10.4242
## mean        10.580   10.3210    9.4827   10.8975   10.6923   10.1547   10.1652
## q3          11.610   11.3498   10.5582   11.9761   11.7167   11.2779   11.1874
## max         18.985   18.4698   18.5681   20.0956   20.4154   19.3664   19.5585
## iqr          1.626    1.6117    1.6694    1.6655    1.5565    1.8001    1.6194
## iqr_high    14.048   13.7674   13.0623   14.4744   14.0515   13.9780   13.6164
## iqr_low     -2.438   -2.4176   -2.5042   -2.4983   -2.3348   -2.7002   -2.4291
## sd           1.919    1.9263    1.9617    2.0548    1.9357    2.0123    1.9054
## var          3.682    3.7105    3.8481    4.2222    3.7468    4.0492    3.6305
## stdvar       0.348    0.3595    0.4058    0.3874    0.3504    0.3987    0.3572
##          TMRC20101 TMRC20092 TMRC20082 TMRC20102 TMRC20099 TMRC20100 TMRC20091
## min         0.0000    0.0000    0.0000    0.0000    0.0000    0.0000    0.0000
## q1          9.5906    9.5162    9.5118    9.3641    9.1497    9.9024    8.8138
## median     10.4954   10.3961   10.4564   10.3106    9.9915   10.7423    9.6165
## mean       10.2282   10.1162   10.1862   10.0469    9.7298   10.4612    9.3844
## q3         11.2829   11.1561   11.2822   11.1693   10.7721   11.5236   10.3869
## max        19.1175   18.9750   20.8048   19.0707   18.9022   19.6087   18.3910
## iqr         1.6924    1.6399    1.7705    1.8052    1.6224    1.6212    1.5732
## iqr_high   13.8215   13.6159   13.9379   13.8770   13.2057   13.9553   12.7467
## iqr_low    -2.5385   -2.4598   -2.6557   -2.7077   -2.4335   -2.4318   -2.3597
## sd          1.9196    1.9276    2.0100    2.0504    1.9574    1.9952    1.8832
## var         3.6848    3.7157    4.0400    4.2042    3.8313    3.9807    3.5466
## stdvar      0.3603    0.3673    0.3966    0.4185    0.3938    0.3805    0.3779
##          TMRC20084 TMRC20087 TMRC20103 TMRC20104 TMRC20086 TMRC20107 TMRC20081
## min         0.0000    0.0000    0.0000    0.0000    0.0000    0.0000    0.0000
## q1          9.3242    9.2479    9.7846    9.8392    9.5608    9.7879    9.9381
## median     10.2252   10.1183   10.6799   10.7271   10.4853   10.7474   10.8313
## mean        9.9834    9.8869   10.4520   10.4655   10.2764   10.4811   10.6037
## q3         11.0607   10.9436   11.5578   11.5908   11.3872   11.6208   11.6733
## max        18.6469   18.6005   18.4626   19.7815   20.3414   20.8640   20.1558
## iqr         1.7365    1.6957    1.7732    1.7516    1.8264    1.8329    1.7352
## iqr_high   13.6655   13.4871   14.2176   14.2182   14.1268   14.3701   14.2761
## iqr_low    -2.6048   -2.5435   -2.6598   -2.6274   -2.7396   -2.7493   -2.6028
## sd          1.9256    1.9267    1.9969    2.0768    1.9951    2.0700    1.9501
## var         3.7078    3.7122    3.9875    4.3133    3.9803    4.2850    3.8030
## stdvar      0.3714    0.3755    0.3815    0.4121    0.3873    0.4088    0.3586
##          TMRC20095
## min         0.0000
## q1          7.8704
## median      8.7912
## mean        8.4484
## q3          9.4979
## max        18.9381
## iqr         1.6275
## iqr_high   11.9391
## iqr_low    -2.4412
## sd          1.9317
## var         3.7315
## stdvar      0.4417
dev <- pp(file = "images/lp_se_boxplot.png", width = 16, height = 9)
lp_box
## Plot describing the gene distribution from a dataset.
closed <- dev.off()
lp_box
## Plot describing the gene distribution from a dataset.

The distribution of observed counts / gene for all samples was plotted as a boxplot on the log2 (it looks like it is log10, but I checked) scale. In contrast to host transcriptome distribution, the parasite distribution of reads/gene is log-normal.

filter_plot <- plot_libsize_prepost(lp_se)
filter_plot$lowgene_plot
## Warning: Using alpha for a discrete variable is not advised.

filter_plot$count_plot

The numbers of genes removed by low-count filtering is drastically lower in parasite samples than human. Thus, even though the range of coverage for the parasite samples is from near 0 to ~ 150 CPM, the number of genes removed by the default low-count filter ranges only from 40 to 129, and the number of reads associated with them ranges only from 100 to 3168.

table(colData(lp_se)[["zymodemecategorical"]])
## 
## z21 z22 z23 z24 
##   7  42  41   2
table(colData(lp_se)[["clinicalresponse"]])
## 
##    cure failure      nd 
##      40      34      18

2 Transcriptome visualizations

2.1 Distribution Visualizations

Najib’s favorite plots are of course the PCA/TNSE. These are nice to look at in order to get a sense of the relationships between samples. They also provide a good opportunity to see what happens when one applies different normalizations, surrogate analyses, filters, etc. In addition, one may set different experimental factors as the primary ‘condition’ (usually the color of plots) and surrogate ‘batches’.

2.2 By Susceptilibity

Column ‘Q’ in the sample sheet, make a categorical version of it with these parameters:

  • 0 <= x <= 35 is resistant
  • 36 <= x <= 48 is ambiguous
  • 49 <= x is sensitive
strain_norm <- normalize(lp_strain, norm = "quant", transform = "log2",
                         convert = "cpm", filter = TRUE)
## Removing 149 low-count genes (8629 remaining).
## transform_counts: Found 96 values equal to 0, adding 1 to the matrix.
zymo_pca <- plot_pca(strain_norm, plot_title = "PCA of parasite expression values",
                     plot_labels = FALSE)
zymo_pca
## The result of performing a fast_svd dimension reduction.
## The x-axis is PC1 and the y-axis is PC2
## Colors are defined by z2.1, z2.2, z2.3, z2.4
## Shapes are defined by resistant, sensitive.

dev <- pp(file = "figures/promastigote_zymocol_sensshape_z21_to_z24.pdf")
zymo_pca$plot
closed <- dev.off()

lp_strain_known <- subset_se(lp_strain, subset = "clinicalcategorical!='unknown'")
strain_known_norm <- normalize(lp_strain_known, norm = "quant", transform = "log2",
                               convert = "cpm", filter = TRUE)
## Removing 154 low-count genes (8624 remaining).
## transform_counts: Found 32 values equal to 0, adding 1 to the matrix.
zymo_known_pca <- plot_pca(strain_known_norm, plot_title = "PCA of parasite expression values",
                           plot_labels = FALSE)
zymo_known_pca
## The result of performing a fast_svd dimension reduction.
## The x-axis is PC1 and the y-axis is PC2
## Colors are defined by z2.1, z2.2, z2.3, z2.4
## Shapes are defined by resistant, sensitive.

dev <- pp(file = "figures/promastigote_zymocol_sensshape_z21_to_z24_only_known_clinical.pdf")
zymo_known_pca$plot
closed <- dev.off()

2.3 Limit to three strains: 2.1/2.2/2.3

only_three_types <- subset_se(lp_strain,
                              subset = "condition=='z2.1'|condition=='z2.3'|condition=='z2.2'")
only_three_norm <- normalize(only_three_types, norm = "quant", transform = "log2",
                             convert = "cpm", batch = FALSE, filter = TRUE) %>%
  set_batches(fact = "phase")
## Removing 149 low-count genes (8629 remaining).
## transform_counts: Found 96 values equal to 0, adding 1 to the matrix.
## The number of samples by batch are:
## 
## Stationary 
##         90
onlythree_pca <- plot_pca(only_three_norm, plot_labels = FALSE,
                          plot_title = "PCA of z2.1, z2.2 and z2.3 parasite expression values")
pp(file = "images/promastigote_threetypes_zymocol_noshape.png")
onlythree_pca$plot
dev.off()
## png 
##   2
onlythree_pca
## The result of performing a fast_svd dimension reduction.
## The x-axis is PC1 and the y-axis is PC2
## Colors are defined by z2.1, z2.2, z2.3
## Shapes are defined by Stationary.

2.4 By my ML knn classifier!

I added the result from my kmer classifier to the sample sheet, let us see how that looks.

lp_strain_knn <- set_conditions(lp_strain, fact = "knnv2classification")
## The numbers of samples by condition are:
## 
## unknown     z21     z22     z23     z24 
##       1       5      43      41       2
strain_norm_knn <- normalize(lp_strain_knn, norm = "quant", transform = "log2",
                             convert = "cpm", filter = TRUE)
## Removing 149 low-count genes (8629 remaining).
## transform_counts: Found 96 values equal to 0, adding 1 to the matrix.
zymo_pca_knn <- plot_pca(strain_norm_knn, plot_title = "PCA of parasite expression values",
                         plot_labels = FALSE)
dev <- pp(file = "images/promastigote_zymocol_sensshape_knnv2.png")
zymo_pca_knn$plot
closed <- dev.off()
zymo_pca_knn
## The result of performing a fast_svd dimension reduction.
## The x-axis is PC1 and the y-axis is PC2
## Colors are defined by unknown, z21, z22, z23, z24
## Shapes are defined by resistant, sensitive.

strain_nobatch <- set_batches(strain_norm, fact = "sourcelab")
## The number of samples by batch are:
## 
## MAG 
##  92
zymo_pcav2 <- plot_pca(strain_nobatch, plot_title = "PCA of parasite expression values",
                       plot_labels = FALSE)
dev <- pp(file = "images/promastigote_zymocol_nobatch.png")
zymo_pcav2$plot
closed <- dev.off()
zymo_pcav2
## The result of performing a fast_svd dimension reduction.
## The x-axis is PC1 and the y-axis is PC2
## Colors are defined by z2.1, z2.2, z2.3, z2.4
## Shapes are defined by MAG.

strain_nb <- normalize(lp_strain, convert = "cpm", transform = "log2",
                       filter = TRUE, batch = "svaseq")
## Removing 149 low-count genes (8629 remaining).
## transform_counts: Found 541 values less than 0.
## transform_counts: Found 541 values equal to 0, adding 1 to the matrix.
strain_nb_pca <- plot_pca(strain_nb, plot_title = "PCA of parasite expression values",
                          plot_labels = FALSE)
dev <- pp(file = "images/clinical_nb_pca_sus_shape.png")
strain_nb_pca$plot
closed <- dev.off()
strain_nb_pca
## The result of performing a fast_svd dimension reduction.
## The x-axis is PC1 and the y-axis is PC2
## Colors are defined by z2.1, z2.2, z2.3, z2.4
## Shapes are defined by resistant, sensitive.

2.4.1 Silly plotly

plotly_plot <- plotly::ggplotly(zymo_pca_knn$plot)
print(plotly_plot)

Add explicit labels for a few reference strains:

  • TMRC20023: Excluded due to coverage (only 7k reads)
  • TMRC20006: This one has 19,815,673 reads, but a weirdly small number of genes and got excluded.
  • TMRC20029: This has 1,946,986 reads and so was excluded.
  • TMRC20034: Not sequenced

** NOTE ** These samples were all removed from examination in the sample_sheet in 202404 and so will not appear in this plot. Thus I am turning off the following block.

samples_to_label <- c("TMRC20023", "TMRC20006", "TMRC20029", "TMRC20007", "TMRC20034",
                      "TMRC20008", "TMRC20027", "TMRC20028", "TMRC20032", "TMRC20040")

label_entries <- zymo_pca$table[samples_to_label, ]
zymo_pca$plot +
  geom_text(mapping = aes(x = "PC1", y = "PC2", label = "sampleid"),
            data = label_entries)

Some likely text for a figure legend might include something like the following (paraphrased from Najib’s 2016 dual transcriptome profiling paper (10.1128/mBio.00027-16)):

Expression profiles of the promastigote samples across multiple strains. Each glyph represents one sample, colors delineate the various strains and fall into two primary clades. Red samples are zymodeme 2.3, blue samples are zymodeme 2.2. The difference between these two primary groups make up approximately 17% of the variance in the PCA. Purple samples are Leishmania braziliensis or zymodeme 1.0/1.5 samples, orange are z2.4, browns and greys are z2.1, z2.0, z3.0, and z3.2 respectively. This analysis was performed following a low-count filter, cpm conversion, quantile normalization, and a log2 transformation. No batch factor was used, nor was a surrogate variable estimation performed.

Some interpretation for this figure might include:

When PCA was performed on the promastigote samples, the dominant (but still relatively small amount of variance) component observed coincided with the two primary strain groups, zymodeme 2.2 and 2.3. With the exception of some Leishmania braziliensis samples, all promatigote samples assayed fell into one of these two categories.

When surrogate varialbe estimation was performed on the entire set of samples, it increased the apparent strain-dependent variance, but had some potentially problematic effects for a couple of samples (one z2.3 sample now lies with the other z2.2 samples); it is assumed that this is because sva attempted to estimate surrogate values for the less-represented strains with some unintended consequences for sample TMRC20095 (which, along with TMRC20008 are the two least covered samples by a significant margin); this hypothesis may be tested by excluding the braziliensis and non-z2.2/2.3 samples and repeating (when this is performed later in the document, the difference between the two primary clades increases to 49.33% of the variance and there are no odd samples).

zymo_tsne <- plot_tsne(strain_norm, plot_title = "TSNE of parasite expression values")
zymo_tsne
## The result of performing a tsne dimension reduction.
## The x-axis is PC1 and the y-axis is PC2
## Colors are defined by z2.1, z2.2, z2.3, z2.4
## Shapes are defined by resistant, sensitive.

strain_nb_tsne <- plot_tsne(strain_nb, plot_title = "TSNE of parasite expression values")
strain_nb_tsne
## The result of performing a tsne dimension reduction.
## The x-axis is PC1 and the y-axis is PC2
## Colors are defined by z2.1, z2.2, z2.3, z2.4
## Shapes are defined by resistant, sensitive.

corheat <- plot_corheat(strain_norm, plot_title = "Correlation heatmap of parasite
                 expression values
")
corheat
## A heatmap of pairwise sample correlations ranging from: 
## 0.642203267746696 to 0.992867624966404.

disheat <- plot_disheat(strain_norm, plot_title = "Distance heatmap of parasite
                 expression values
")
disheat$plot

plot_sm(strain_norm)
## When the standard median metric was plotted, the values observed range
## from 0.642203267746696 to 1 with quartiles at 0.932012660770469 and 0.944521289249787.

Potential start for a figure legend:

Global relationships among the promastigote transcriptional profiles. Pairwise pearson correlations and Euclidean distances were calculated using the normalized expression matrices. Colors along the top row delineate the experimental conditions (same colors as the PCA) Samples were clustered by nearest neighbor clustering and each colored tile describes one correlation value between two samples (red to white delineates pearson correlation values of the 8,710 normalized gene values between two samples ranging from <= 0.7 to >= 1.0) or the euclidean distance between two samples (dark blue to white delineates identical to a normalized euclidean distance of >= 110).

Some interpretation for this figure might include:

When the global relationships among the samples were distilled down to individual euclidean distances or pearson correlation coefficients between pairs of samples, the primary clustering among samples observed was according to strain. The primary significant outlier sample (TMRC20095) is explicitly due to low coverage. The other outlier strains are either braziliensis (purple) or a series of strains which, when viewed in IGV, appear to have genetic variants which bridge the differences between the two primary zymodemes, particularly on the known aneuploid chromosomes.

2.5 Limit to just two strains: 2.2/2.3

lp_two_strains_norm <- normalize(lp_zymo, norm = "quant", transform = "log2",
                                 convert = "cpm", batch = FALSE, filter = TRUE)
## Removing 150 low-count genes (8628 remaining).
## transform_counts: Found 96 values equal to 0, adding 1 to the matrix.
onlytwo_pca <- plot_pca(lp_two_strains_norm, plot_title = "PCA of z2.2 and z2.3 parasite expression values",
                        plot_labels = FALSE)
dev <- pp(file = "figures/zymo_z2.2_z2.3_pca_sus_shape.pdf")
onlytwo_pca$plot
closed <- dev.off()
onlytwo_pca$plot

lp_two_strains_known <- subset_se(lp_zymo, subset = "clinicalcategorical!='unknown'")
lp_two_strains_known_norm <- normalize(lp_two_strains_known, norm = "quant", transform = "log2",
                                       convert = "cpm", batch = FALSE, filter = TRUE)
## Removing 155 low-count genes (8623 remaining).
## transform_counts: Found 32 values equal to 0, adding 1 to the matrix.
onlytwo_known_pca <- plot_pca(lp_two_strains_known_norm, plot_labels = FALSE,
                              plot_title = "PCA of z2.2 and z2.3 parasite expression values")
dev <- pp(file = "figures/zymo_z2.2_z2.3_pca_sus_shape_only_known.pdf")
onlytwo_pca$plot
closed <- dev.off()
onlytwo_pca
## The result of performing a fast_svd dimension reduction.
## The x-axis is PC1 and the y-axis is PC2
## Colors are defined by z2.2, z2.3
## Shapes are defined by undefined.

lp_two_strains_nb <- normalize(lp_zymo, transform = "log2", convert = "cpm",
                               batch = "svaseq", filter = TRUE)
## Removing 150 low-count genes (8628 remaining).
## transform_counts: Found 512 values less than 0.
## transform_counts: Found 512 values equal to 0, adding 1 to the matrix.
onlytwo_pca_nb <- plot_pca(lp_two_strains_nb, plot_labels = FALSE,
                           plot_title = "PCA of z2.2 and z2.3 parasite expression values")
dev <- pp(file = "images/zymo_z2.2_z2.3_pca_sus_shape_nb.pdf")
onlytwo_pca_nb$plot
closed <- dev.off()
onlytwo_pca_nb$plot

2.6 By Cure/Fail status

This is by far the most problematic comparison, I think the only interpretation of the following images is that the parasite has little effect on the likelihood that a person will successfully end treatment. There does appear to be some variance associated with cure/fail, but only in a few samples (visible in ~10 fail samples and perhaps ~8 cure samples when sva is applied to the data).

cf_norm <- normalize(lp_cf, convert = "cpm", transform = "log2",
                     norm = "quant", filter = TRUE)
## Removing 149 low-count genes (8629 remaining).
## transform_counts: Found 96 values equal to 0, adding 1 to the matrix.
start_cf <- plot_pca(cf_norm, plot_title = "PCA of parasite expression values",
                     plot_labels = FALSE)
dev <- pp(file = "figures/cure_fail_sus_shape_all.pdf")
start_cf$plot
closed <- dev.off()
start_cf
## The result of performing a fast_svd dimension reduction.
## The x-axis is PC1 and the y-axis is PC2
## Colors are defined by cure, fail, unknown
## Shapes are defined by resistant, sensitive.

lp_cf_known <- subset_se(lp_cf, subset = "clinicalcategorical!='unknown'")
cf_known_norm <- normalize(lp_cf_known, convert = "cpm", transform = "log2",
                           norm = "quant", filter = TRUE)
## Removing 154 low-count genes (8624 remaining).
## transform_counts: Found 32 values equal to 0, adding 1 to the matrix.
start_cf_known <- plot_pca(cf_known_norm, plot_title = "PCA of parasite expression values",
                           plot_labels = FALSE)
dev <- pp(file = "figures/cure_fail_sus_shape_known.pdf")
start_cf_known$plot
closed <- dev.off()
start_cf_known
## The result of performing a fast_svd dimension reduction.
## The x-axis is PC1 and the y-axis is PC2
## Colors are defined by cure, fail
## Shapes are defined by resistant, sensitive.

only_two_cf <- set_conditions(lp_zymo, fact = "clinicalcategorical",
                              colors = color_choices[["cf"]]) %>%
  set_batches(fact = "sus_category_current")
## The numbers of samples by condition are:
## 
##    cure    fail unknown 
##      33      32      18
## Warning in set_se_colors(new_se, colors = colors): Colors for the following
## categories are not being used: notapplicable.
## The number of samples by batch are:
## 
## resistant sensitive 
##        44        39
only_two_cf_norm <- normalize(only_two_cf, norm = "quant", transform = "log2",
                              convert = "cpm", batch = FALSE, filter = TRUE)
## Removing 150 low-count genes (8628 remaining).
## transform_counts: Found 96 values equal to 0, adding 1 to the matrix.
only_two_cf_pca <- plot_pca(only_two_cf_norm, plot_labels = FALSE,
                            plot_title = "PCA of z2.2 and z2.3 parasite expression values")
dev <- pp(file = "figures/cure_fail_sus_shape_onlyz22_z23.pdf")
only_two_cf_pca$plot
dev.off()
## png 
##   2
only_two_cf_pca
## The result of performing a fast_svd dimension reduction.
## The x-axis is PC1 and the y-axis is PC2
## Colors are defined by cure, fail, unknown
## Shapes are defined by resistant, sensitive.

only_two_cf_known <- subset_se(only_two_cf, subset = "condition!='unknown'")
only_two_cf_known_norm <- normalize(only_two_cf_known, norm = "quant", transform = "log2",
                                    convert = "cpm", batch = FALSE, filter = TRUE)
## Removing 155 low-count genes (8623 remaining).
## transform_counts: Found 32 values equal to 0, adding 1 to the matrix.
only_two_cf_known_pca <- plot_pca(only_two_cf_known_norm, plot_labels = FALSE,
                                  plot_title = "PCA of z2.2 and z2.3 parasite expression values")
dev <- pp(file = "figures/cure_fail_sus_shape_onlyz22_z23_known.pdf")
only_two_cf_known_pca$plot
dev.off()
## png 
##   2
only_two_cf_known_pca
## The result of performing a fast_svd dimension reduction.
## The x-axis is PC1 and the y-axis is PC2
## Colors are defined by cure, fail
## Shapes are defined by resistant, sensitive.

cf_nb <- normalize(lp_cf, convert = "cpm", transform = "log2",
                   filter = TRUE, batch = "svaseq")
## Removing 149 low-count genes (8629 remaining).
## transform_counts: Found 292 values less than 0.
## transform_counts: Found 292 values equal to 0, adding 1 to the matrix.
cf_nb_pca <- plot_pca(cf_nb, plot_title = "PCA of parasite expression values",
                      plot_labels = FALSE)
dev <- pp(file = "images/cf_sus_share_nb.png")
cf_nb_pca$plot
closed <- dev.off()
cf_nb_pca
## The result of performing a fast_svd dimension reduction.
## The x-axis is PC1 and the y-axis is PC2
## Colors are defined by cure, fail, unknown
## Shapes are defined by resistant, sensitive.

cf_norm <- normalize(lp_cf, transform = "log2", convert = "cpm",
                     filter = TRUE, norm = "quant")
## Removing 149 low-count genes (8629 remaining).
## transform_counts: Found 96 values equal to 0, adding 1 to the matrix.
## Getting an error which really does not make sense, I ran it manually and it worked fine.
test <- pca_information(cf_norm, num_components = 6, plot_pcas = TRUE,
                        factors = c("clinicalcategorical", "zymodemecategorical",
                                    "pathogenstrain", "passagenumber"))
test$anova_p
##                           PC1    PC2    PC3      PC4       PC5     PC6
## clinicalcategorical 9.168e-02 0.4286 0.1710 0.185702 7.118e-01 0.18993
## zymodemecategorical 7.306e-29 0.2921 0.5239 0.373609 7.239e-01 0.86261
## pathogenstrain      2.624e-01 0.1768 0.1649 0.004742 3.411e-01 0.98471
## passagenumber       9.328e-01 0.2103 0.4129 0.001469 2.004e-14 0.02395
test$cor_heatmap

2.7 By Current drug sensitivity assay data

We have two competing metrics of antmonial sensitivity; one historical and one current. In both cases there is a reasonable expectation that resistant strains tend to be zymodeme 2.3 and sensitive strains tend to be zymodeme 2.2. There appear to be more exceptions to this rule of thumb in the current data than the historical.

dim(assay(lp_susceptibility))
## [1] 8778   92
sus_norm <- normalize(lp_susceptibility, transform = "log2", convert = "cpm",
                      norm = "quant", filter = TRUE)
## Removing 149 low-count genes (8629 remaining).
## transform_counts: Found 96 values equal to 0, adding 1 to the matrix.
sus_pca <- plot_pca(sus_norm, plot_title = "PCA of parasite expression values",
                    plot_labels = FALSE)
dev <- pp(file = "figures/sus_norm_pca.svg")
sus_pca[["plot"]]
closed <- dev.off()
dev <- pp(file = "figures/sus_norm_pca.pdf")
sus_pca[["plot"]]
closed <- dev.off()
sus_pca
## The result of performing a fast_svd dimension reduction.
## The x-axis is PC1 and the y-axis is PC2
## Colors are defined by resistant, sensitive
## Shapes are defined by cure, fail, unknown.

lp_susceptibility_known <- subset_se(lp_susceptibility, subset = "batch!='unknown'")
sus_known_norm <- normalize(lp_susceptibility_known, transform = "log2", convert = "cpm",
                            norm = "quant", filter = TRUE)
## Removing 154 low-count genes (8624 remaining).
## transform_counts: Found 32 values equal to 0, adding 1 to the matrix.
sus_known_pca <- plot_pca(sus_known_norm, plot_title = "PCA of parasite expression values",
                          plot_labels = FALSE)
dev <- pp(file = "figures/sus_norm_known_pca.pdf")
sus_known_pca[["plot"]]
closed <- dev.off()
sus_known_pca
## The result of performing a fast_svd dimension reduction.
## The x-axis is PC1 and the y-axis is PC2
## Colors are defined by resistant, sensitive
## Shapes are defined by cure, fail.

lp_sus_two <- subset_se(lp_susceptibility, subset = "zymodemecategorical!='z21'") %>%
  subset_se(subset = "zymodemecategorical!='z24'")
sus_two_norm <- normalize(lp_sus_two, transform = "log2", convert = "cpm",
                          norm = "quant", filter = TRUE)
## Removing 150 low-count genes (8628 remaining).
## transform_counts: Found 96 values equal to 0, adding 1 to the matrix.
sus_two_pca <- plot_pca(sus_two_norm, plot_title = "PCA of parasite expression values",
                        plot_labels = FALSE)
dev <- pp(file = "figures/sus_norm_two_pca.pdf")
sus_two_pca[["plot"]]
closed <- dev.off()
sus_two_pca
## The result of performing a fast_svd dimension reduction.
## The x-axis is PC1 and the y-axis is PC2
## Colors are defined by resistant, sensitive
## Shapes are defined by cure, fail, unknown.

lp_sus_two_known <- subset_se(lp_sus_two, subset = "clinicalcategorical!='unknown'")
sus_two_known_norm <- normalize(lp_sus_two_known, transform = "log2", convert = "cpm",
                                norm = "quant", filter = TRUE)
## Removing 155 low-count genes (8623 remaining).
## transform_counts: Found 32 values equal to 0, adding 1 to the matrix.
sus_two_known_pca <- plot_pca(sus_two_known_norm, plot_title = "PCA of parasite expression values",
                              plot_labels = FALSE)
dev <- pp(file = "figures/sus_norm_two_known_pca.pdf")
sus_two_known_pca[["plot"]]
closed <- dev.off()
sus_two_known_pca
## The result of performing a fast_svd dimension reduction.
## The x-axis is PC1 and the y-axis is PC2
## Colors are defined by resistant, sensitive
## Shapes are defined by cure, fail.

sus_nb <- normalize(lp_susceptibility, transform = "log2", convert = "cpm",
                    batch = "svaseq", filter = TRUE)
## Removing 149 low-count genes (8629 remaining).
## transform_counts: Found 563 values less than 0.
## transform_counts: Found 563 values equal to 0, adding 1 to the matrix.
sus_nb_pca <- plot_pca(sus_nb, plot_title = "PCA of parasite expression values",
                       plot_labels = FALSE)
dev <- pp(file = "images/sus_nb_pca.png")
sus_nb_pca[["plot"]]
closed <- dev.off()
sus_nb_pca
## The result of performing a fast_svd dimension reduction.
## The x-axis is PC1 and the y-axis is PC2
## Colors are defined by resistant, sensitive
## Shapes are defined by cure, fail, unknown.

2.8 By Historical drug sensitivity assay data

sus_hist_norm <- normalize(lp_susceptibility_historical, transform = "log2", convert = "cpm",
                           norm = "quant", filter = TRUE)
## Removing 149 low-count genes (8629 remaining).
## transform_counts: Found 96 values equal to 0, adding 1 to the matrix.
sus_hist_pca <- plot_pca(sus_hist_norm, plot_title = "PCA of parasite expression values",
                         plot_labels = FALSE)
dev <- pp(file = "images/sus_hist_norm_pca.png")
sus_hist_pca[["plot"]]
## Warning in MASS::cov.trob(data[, vars], wt = weight * nrow(data)): Probable
## convergence failure
## Warning in MASS::cov.trob(data[, vars], wt = weight * nrow(data)): Probable
## convergence failure
closed <- dev.off()
sus_hist_pca
## The result of performing a fast_svd dimension reduction.
## The x-axis is PC1 and the y-axis is PC2
## Colors are defined by ambiguous, resistant, sensitive, unknown
## Shapes are defined by cure, fail, unknown.
## Warning in MASS::cov.trob(data[, vars], wt = weight * nrow(data)): Probable
## convergence failure
## Warning in MASS::cov.trob(data[, vars], wt = weight * nrow(data)): Probable
## convergence failure

sus_hist_nb <- normalize(lp_susceptibility_historical, transform = "log2", convert = "cpm",
                         batch = "svaseq", filter = TRUE)
## Removing 149 low-count genes (8629 remaining).
## transform_counts: Found 298 values less than 0.
## transform_counts: Found 298 values equal to 0, adding 1 to the matrix.
sus_hist_nb_pca <- plot_pca(sus_hist_nb, plot_title = "PCA of parasite expression values",
                            plot_labels = FALSE)
dev <- pp(file = "images/sus_hist_nb_pca.png")
sus_hist_nb_pca[["plot"]]
closed <- dev.off()
sus_hist_nb_pca
## The result of performing a fast_svd dimension reduction.
## The x-axis is PC1 and the y-axis is PC2
## Colors are defined by ambiguous, resistant, sensitive, unknown
## Shapes are defined by cure, fail, unknown.

2.9 Zymodeme enzyme gene IDs

Najib read me an email listing off the gene names associated with the zymodeme classification. I took those names and cross referenced them against the Leishmania panamensis gene annotations and found the following:

They are:

  1. ALAT: LPAL13_120010900 – alanine aminotransferase
  2. ASAT: LPAL13_340013000 – aspartate aminotransferase
  3. G6PD: LPAL13_000054100 – glucase-6-phosphate 1-dehydrogenase
  4. NH: LPAL13_14006100, LPAL13_180018500 – inosine-guanine nucleoside hydrolase
  5. MPI: LPAL13_320022300 (maybe) – mannose phosphate isomerase (I chose phosphomannose isomerase)

Given these 6 gene IDs (NH has two gene IDs associated with it), I can do some looking for specific differences among the various samples.

2.9.1 Expression levels of zymodeme genes

The following creates a colorspace (red to green) heatmap showing the observed expression of these genes in every sample.

my_genes <- c("LPAL13_120010900", "LPAL13_340013000", "LPAL13_000054100",
              "LPAL13_140006100", "LPAL13_180018500", "LPAL13_320022300",
              "other")
my_names <- c("ALAT", "ASAT", "G6PD", "NHv1", "NHv2", "MPI", "other")

zymo_se <- exclude_genes(strain_norm, ids = my_genes, method = "keep")
## Note, I renamed this to subset_genes().
## subset_genes(), before removal, there were 8629 genes, now there are 6.
## There are 92 samples which kept less than 90 percent counts.
## TMRC20001 TMRC20065 TMRC20004 TMRC20005 TMRC20066 TMRC20039 TMRC20037 TMRC20038 
##   0.08587   0.08454   0.08368   0.08346   0.08132   0.08408   0.08142   0.08294 
## TMRC20067 TMRC20068 TMRC20041 TMRC20015 TMRC20009 TMRC20010 TMRC20016 TMRC20011 
##   0.08342   0.08390   0.08245   0.08428   0.08310   0.08372   0.08304   0.08288 
## TMRC20012 TMRC20013 TMRC20017 TMRC20014 TMRC20018 TMRC20019 TMRC20070 TMRC20020 
##   0.08485   0.08515   0.08275   0.08332   0.08290   0.08304   0.08350   0.08154 
## TMRC20021 TMRC20022 TMRC20024 TMRC20036 TMRC20069 TMRC20033 TMRC20026 TMRC20031 
##   0.08139   0.08476   0.08158   0.08203   0.08201   0.08208   0.08690   0.08142 
## TMRC20076 TMRC20073 TMRC20055 TMRC20079 TMRC20071 TMRC20078 TMRC20094 TMRC20042 
##   0.08260   0.08427   0.08367   0.08462   0.08370   0.08320   0.08349   0.08360 
## TMRC20058 TMRC20072 TMRC20059 TMRC20048 TMRC20057 TMRC20088 TMRC20056 TMRC20060 
##   0.08254   0.08334   0.08301   0.08181   0.08540   0.08423   0.08398   0.08254 
## TMRC20077 TMRC20074 TMRC20063 TMRC20053 TMRC20052 TMRC20064 TMRC20075 TMRC20051 
##   0.08337   0.08304   0.08185   0.08225   0.08206   0.08254   0.08315   0.08381 
## TMRC20050 TMRC20049 TMRC20062 TMRC20110 TMRC20080 TMRC20043 TMRC20083 TMRC20054 
##   0.08196   0.08469   0.08361   0.08451   0.08162   0.08284   0.08379   0.08424 
## TMRC20085 TMRC20046 TMRC20093 TMRC20089 TMRC20047 TMRC20090 TMRC20044 TMRC20045 
##   0.08369   0.08478   0.08396   0.08296   0.08368   0.08111   0.08464   0.08318 
## TMRC20105 TMRC20108 TMRC20109 TMRC20098 TMRC20096 TMRC20101 TMRC20092 TMRC20082 
##   0.08388   0.08252   0.08391   0.08428   0.08292   0.08302   0.08254   0.08219 
## TMRC20102 TMRC20099 TMRC20100 TMRC20091 TMRC20084 TMRC20087 TMRC20103 TMRC20104 
##   0.08278   0.08408   0.08265   0.08430   0.08253   0.08380   0.08376   0.08352 
## TMRC20086 TMRC20107 TMRC20081 TMRC20095 
##   0.08305   0.08097   0.08154   0.07737
zymo_heatmap <- plot_sample_heatmap(zymo_se, row_label = my_names)
zymo_heatmap

A recent suggestion included a query about the relationship of our amastigote TMRC2 samples which were the result of infecting a set of macrophages vs. these promastigote samples.

So far, we have kept these two experiments separate, now let us merge them.

tmrc2_macrophage_norm <- normalize(lp_macrophage, transform = "log2", convert = "cpm",
                                   norm = "quant", filter = TRUE)
## Removing 0 low-count genes (8778 remaining).
## transform_counts: Found 3577 values equal to 0, adding 1 to the matrix.
## Hey you, this annotation call should be made automatic for the container!
annotation(lp_se) <- "org.Lpanamensis.MHOMCOL81L13.v46.eg.db"
annotation(lp_macrophage) <- annotation(lp_se)
all_tmrc2 <- hpgltools:::combine_se(lp_se, lp_macrophage)

Before we can use the combined data, we must reconcile a few of aspects of it, notably we need to specify which samples are amastigotes and which are promastigotes.

all_nosb <- all_tmrc2
colData(all_nosb)[["stage"]] <- "promastigote"
na_idx <- is.na(colData(all_nosb)[["macrophagetreatment"]])
colData(all_nosb)[na_idx, "macrophagetreatment"] <- "undefined"
all_nosb <- subset_se(all_nosb, subset = "macrophagetreatment!='inf_sb'")
ama_idx <- colData(all_nosb)[["macrophagetreatment"]] == "inf"
colData(all_nosb)[ama_idx, "stage" ] <- "amastigote"

## Make sure that the zymodeme does not have the inf_ prefix.
zymodeme_char <- gsub(x = colData(all_nosb)[["condition"]], pattern = "^inf_", replacement = "")
colData(all_nosb)[["condition"]] <- zymodeme_char

colData(all_nosb)[["batch"]] <- colData(all_nosb)[["stage"]]
all_nosb <- subset_se(all_nosb, subset = "condition!='none'")
all_norm <- normalize(all_nosb, convert = "cpm", norm = "quant",
                      transform = "log2", filter = TRUE)
## Removing 94 low-count genes (8684 remaining).
## transform_counts: Found 81 values equal to 0, adding 1 to the matrix.
pro_ama_pca <- plot_pca(all_norm)
## Potentially check over the experimental design, there appear to be missing values.
## Warning in plot_pca(data = mtrx, design = design, state = state, plot_colors =
## plot_colors, : There are NA values in the component data.  This can lead to
## weird plotting errors.
pro_ama_pca[["plot"]]

I think the above picture is sort of the opposite of what we want to compare in a DE analysis for this set of data, e.g. we want to compare promastigotes from amastigotes?

two_nosb <- set_batches(all_nosb, fact = "condition") %>%
  set_conditions(fact = "stage") %>%
  subset_se(subset = "batch=='z2.2'|batch=='z2.3'")
## The number of samples by batch are:
## 
## z2.1 z2.2 z2.3 z2.4 
##    7   56   56    2
## The numbers of samples by condition are:
## 
##   amastigote promastigote 
##           29           92
two_norm <- normalize(two_nosb, convert = "cpm", norm = "quant",
                      transform = "log2", filter = TRUE)
## Removing 94 low-count genes (8684 remaining).
## transform_counts: Found 81 values equal to 0, adding 1 to the matrix.
pro_ama_two_pca <- plot_pca(two_norm)
## Potentially check over the experimental design, there appear to be missing values.
## Warning in plot_pca(data = mtrx, design = design, state = state, plot_colors =
## plot_colors, : There are NA values in the component data.  This can lead to
## weird plotting errors.
pro_ama_two_pca[["plot"]]

zy_stage_factor <- paste0(colData(two_nosb)[["batch"]], "_",
                          colData(two_nosb)[["stage"]])
colData(two_nosb)[["zystage"]] <- zy_stage_factor
zystage <- set_conditions(two_nosb, fact = "zystage")
## The numbers of samples by condition are:
## 
##   z2.2_amastigote z2.2_promastigote   z2.3_amastigote z2.3_promastigote 
##                14                42                15                41
zystage_norm <- normalize(zystage, filter = TRUE, norm = "quant",
                          convert = "cpm", transform = "log2")
## Removing 94 low-count genes (8684 remaining).
## transform_counts: Found 81 values equal to 0, adding 1 to the matrix.
plot_pca(zystage_norm)$plot
## Potentially check over the experimental design, there appear to be missing values.
## Warning in plot_pca(data = mtrx, design = design, state = state, plot_colors =
## plot_colors, : There are NA values in the component data.  This can lead to
## weird plotting errors.

zystage_keepers <- list(
  "z2322_ama" = c("z23_amastigote", "z22_amastigote"),
  "z2322_pro" = c("z23_promastigote", "z22_promastigote"),
  "proama_z23" = c("z23_amastigote", "z23_promastigote"),
  "proama_z22" = c("z22_amastigote", "z22_promastigote"))

zystage_de <- all_pairwise(zystage, filter = TRUE, model_batch = "svaseq",
                           model_fstring = "~ 0 + condition")
##   z2.2_amastigote z2.2_promastigote   z2.3_amastigote z2.3_promastigote 
##                14                42                15                41
## Removing 94 low-count genes (8684 remaining).
## Potentially check over the experimental design, there appear to be missing values.
## Warning in plot_pca(data = mtrx, design = design, state = state, plot_colors =
## plot_colors, : There are NA values in the component data.  This can lead to
## weird plotting errors.
## Potentially check over the experimental design, there appear to be missing values.
## Warning in plot_pca(data = mtrx, design = design, state = state, plot_colors =
## plot_colors, : There are NA values in the component data.  This can lead to
## weird plotting errors.
## Basic step 0/3: Normalizing data.
## Basic step 0/3: Converting data.
## I think this is failing? SummarizedExperiment
## Basic step 0/3: Transforming data.
## Setting 12029 entries to zero.
## converting counts to integer mode
## gene-wise dispersion estimates
## mean-dispersion relationship
## final dispersion estimates
## conditions
##   z22_amastigote z22_promastigote   z23_amastigote z23_promastigote 
##               14               42               15               41
## conditions
##   z22_amastigote z22_promastigote   z23_amastigote z23_promastigote 
##               14               42               15               41
## conditions
##   z22_amastigote z22_promastigote   z23_amastigote z23_promastigote 
##               14               42               15               41

zystage_tables <- combine_de_tables(
  zystage_de, keepers = zystage_keepers,
  excel = glue("excel/zymodeme_stage_table-v{ver}.xlsx"))
## Looking for subscript invalid names, start of extract_keepers.
## Looking for subscript invalid names, end of extract_keepers.

3 Gene expression with respect to chromosome

I want to make a plot where the x-axis is the number of genes on a chromosome and the y-axis is the mean of the expression of those genes.

assay_by_chr_plot <- plot_assay_by_chromosome(lp_zymo, chromosome_column = "chromosome")
assay_by_chr_plot[["plot"]]

4 SNP profiles

One potentially interesting aspect of the variant data: it may be able to help us define the zymodeme state of previous, untested samples.

In order to test this, I am loading some of the 2016 data alongside the new TMRC2 data to see if they fit together.

This is using an older dataset for which I am not sure we have permissions to include in the container, so I am turning them off for now.

old_se <- create_se("sample_sheets/tmrc2_samples_20191203.xlsx",
                        file_column = "tophat2file")

tt <- old_se$expressionset
rownames(tt) <- gsub(pattern = "^exon_", replacement = "", x = rownames(tt))
rownames(tt) <- gsub(pattern = "\\.1$", replacement = "", x = rownames(tt))
old_se$expressionset <- tt
rm(tt)

4.1 Create the SNP expressionset

One other important caveat, we have a group of new samples which have not yet run through the variant search pipeline, so I need to remove them from consideration. Though it looks like they finished overnight…

In the non-containerized version of this document, the following block combines an older dataset with the current data.

both_norm <- normalize(new_snps_sufficient, transform = "log2", norm = "quant") %>%
  set_conditions(fact = "pathogenstrain")
## transform_counts: Found 79143354 values equal to 0, adding 1 to the matrix.
## The numbers of samples by condition are:
## 
##   10070   10750   10772   10977   11006   11024   11026   11028   11031   11045 
##       1       1       1       1       1       1       1       1       1       1 
##   11071   11075   11090   11108   11109 11126-I   11133   11134   11152    1131 
##       1       1       1       1       1       1       1       1       1       1 
##   12116   12166   12169 12218-I   12251   12309   12312   12355   12367   12371 
##       1       1       1       1       1       1       1       1       1       1 
##   12417   12444   12479   12535   12554   12556   12570   12578   12581   12588 
##       1       1       1       1       1       1       1       1       1       1 
##   13464   13473   13474   13582   13589   13595   13597   13625   13631   13703 
##       1       1       1       1       1       1       1       1       1       1 
##   13720   13740   13787   13794   13978   14016   14056   14096   14103   14111 
##       1       1       1       1       1       1       1       1       1       1 
##   14149    2122    2168    2173    2183    2198    2272    2330    2331    2411 
##       1       1       1       1       1       1       1       1       1       1 
##    2414    2423    2429    2439    2472    2482    2496    2500    3117    4700 
##       1       1       1       1       1       1       1       1       1       1 
##    4745    4810    4829    4830    4876    5986    6957    7011    7105    7158 
##       1       1       1       1       1       1       1       1       1       1 
##    8190 
##       1

The data structure ‘both_norm’ now contains our 2016 data along with the newer data collected since 2019.

4.2 Plot of SNP profiles for zymodemes

The following plot shows the SNP profiles of all samples (old and new) where the colors at the top show either the 2.2 strains (orange), 2.3 strains (green), the previous samples (purple), or the various lab strains (pink etc).

new_variant_heatmap <- plot_disheat(new_snps_sufficient)
dev <- pp(file = "images/raw_snp_disheat.png", height = 12, width = 12)
new_variant_heatmap$plot
closed <- dev.off()
new_variant_heatmap$plot

The function get_snp_sets() takes the provided metadata factor (in this case ‘condition’) and looks for variants which are exclusive to each element in it. In this case, this is looking for differences between 2.2 and 2.3, as well as the set shared among them.

snp_sets <- get_snp_sets(new_snps_sufficient, factor = "condition")
## The samples represent the following categories:
## 
## z2.1 z2.2 z2.3 z2.4 
##    7   42   40    2
## Using a proportion of observed variants, converting the data to binary observations.
## The factor z2.1 has 7 rows.
## The factor z2.2 has 42 rows.
## The factor z2.3 has 40 rows.
## The factor z2.4 has 2 rows.
## Finished iterating over the chromosomes.
snp_sets
## A set of variants observed when cross referencing all variants against
## the samples associated with each metadata factor: condition.  4
## categories and 927126 variants were observed with 15
## combinations among them.  725 chromosomes/scaffolds were observed with a
## density of variants ranging from 0.000652315720808871 to 0.114678899082569.
##Biobase::annotation(old_se$expressionset) = Biobase::annotation(lp_se$expressionset)
##both_se <- combine_ses(lp_se, old_se)

snp_genes <- snps_vs_genes(lp_se, snp_sets, chr_column = exp_chr_col,
                           start_column = exp_start_col, end_column = exp_end_col)
## The snp grange data has 927126 elements.
## The first few snp chromosomes are: LPAL13_SCAF000001, LPAL13_SCAF000002, LPAL13_SCAF000003, LPAL13_SCAF000004, LPAL13_SCAF000005, LPAL13_SCAF000007
## The first few exp chromosomes are: LPAL13_SCAF000001, LPAL13_SCAF000003, LPAL13_SCAF000010, LPAL13_SCAF000011, LPAL13_SCAF000017, LPAL13_SCAF000021
## There are 437555 overlapping variants and genes.
## I think we have some metrics here we can plot...
snp_subset <- snp_subset_genes(
  lp_se, new_snps_sufficient, start_column = exp_start_col, end_column = exp_end_col,
  exp_name_column = exp_chr_col,
  genes = c("LPAL13_120010900", "LPAL13_340013000", "LPAL13_000054100",
            "LPAL13_140006100", "LPAL13_180018500", "LPAL13_320022300"))
## subset_genes(), before removal, there were 927126 genes, now there are 85.
## There are 91 samples which kept less than 90 percent counts.
## TMRC20001 TMRC20065 TMRC20004 TMRC20005 TMRC20066 TMRC20039 TMRC20037 TMRC20038 
## 0.0363994 0.0284342 0.0704007 0.0446300 0.0244539 0.0218095 0.0228205 0.0244650 
## TMRC20067 TMRC20068 TMRC20041 TMRC20015 TMRC20009 TMRC20010 TMRC20016 TMRC20011 
## 0.0259861 0.0275633 0.0084708 0.0249880 0.0000000 0.0278667 0.0232143 0.0243409 
## TMRC20012 TMRC20013 TMRC20017 TMRC20014 TMRC20018 TMRC20019 TMRC20070 TMRC20020 
## 0.0778398 0.0294979 0.0102837 0.0191370 0.0239034 0.0282985 0.0274939 0.0235432 
## TMRC20021 TMRC20022 TMRC20024 TMRC20036 TMRC20069 TMRC20033 TMRC20026 TMRC20031 
## 0.0286477 0.0000000 0.0212984 0.0089603 0.0270318 0.0019682 0.0352553 0.0199682 
## TMRC20076 TMRC20073 TMRC20055 TMRC20079 TMRC20071 TMRC20078 TMRC20094 TMRC20042 
## 0.0270505 0.0282364 0.0395199 0.0280768 0.0247556 0.0177539 0.0279169 0.0398656 
## TMRC20058 TMRC20072 TMRC20059 TMRC20048 TMRC20057 TMRC20088 TMRC20056 TMRC20060 
## 0.0256906 0.0158464 0.0251221 0.0238737 0.0062348 0.0349161 0.0003009 0.0294943 
## TMRC20077 TMRC20074 TMRC20063 TMRC20053 TMRC20052 TMRC20064 TMRC20075 TMRC20051 
## 0.0340198 0.0282781 0.0017690 0.0202252 0.0274156 0.0280939 0.0236363 0.0297070 
## TMRC20050 TMRC20049 TMRC20062 TMRC20110 TMRC20080 TMRC20043 TMRC20083 TMRC20054 
## 0.0324800 0.0338522 0.0314505 0.0350400 0.0288995 0.0273341 0.0121428 0.0298779 
## TMRC20085 TMRC20046 TMRC20093 TMRC20089 TMRC20047 TMRC20090 TMRC20044 TMRC20045 
## 0.0251593 0.0054459 0.0065508 0.0269888 0.0299476 0.0273432 0.0316706 0.0052405 
## TMRC20105 TMRC20108 TMRC20109 TMRC20098 TMRC20096 TMRC20101 TMRC20092 TMRC20102 
## 0.0303013 0.0267368 0.0184037 0.0265627 0.0202590 0.0282691 0.0024627 0.0250142 
## TMRC20099 TMRC20100 TMRC20091 TMRC20084 TMRC20087 TMRC20103 TMRC20104 TMRC20086 
## 0.0291960 0.0266043 0.0274472 0.0063494 0.0291851 0.0065616 0.0265140 0.0251956 
## TMRC20107 TMRC20081 TMRC20095 
## 0.0200527 0.0133293 0.0136510
tt <- normalize(snp_subset, transform = "log2", filter = TRUE)
## Removing 0 low-count genes (85 remaining).
## transform_counts: Found 7122 values equal to 0, adding 1 to the matrix.
zymo_heat <- plot_sample_heatmap(tt, row_label = rownames(assay(snp_subset)))
zymo_heat

4.3 Compare variants to DE genes

Najib has asked a few times about the relationship between variants and DE genes. In subsequent conversations I figured out what he really wants to learn is variants in the UTR (most likely 5’) which might affect expression of genes. The following explicitly does not help this question, but is a paralog: is there a relationship between variants in the CDS and differential expression?

4.3.1 Collect DE data

In order to do this comparison, we need to reload some of the DE results.

These blocks need to be moved to post-differential analyses

rda <- glue("rda/zymo_tables_sva-v{ver}.rda")
varname <- gsub(x = basename(rda), pattern = "\\.rda", replacement = "")
loaded <- load(file = rda)
zy_df <- get0(varname)[["data"]][["zymodeme"]]
vars_df <- data.frame(ID = names(snp_genes$summary_by_gene),
                      variants = as.numeric(snp_genes$summary_by_gene))
vars_df[["variants"]] <- log2(vars_df[["variants"]] + 1)
vars_by_de_gene <- merge(zy_df, vars_df, by.x = "row.names", by.y = "ID")
cor.test(vars_by_de_gene$deseq_logfc, vars_by_de_gene$variants)
variants_wrt_logfc <- plot_linear_scatter(vars_by_de_gene[, c("deseq_logfc", "variants")])
variants_wrt_logfc$scatter
## It looks like there might be some genes of interest, even though this is not actually
## the question of interest.

Didn’t I create a set of densities by chromosome? Oh I think they come in from get_snp_sets()

4.4 SNPS associated with clinical response in the TMRC samples

clinical_sets <- get_snp_sets(new_snps_sufficient, factor = "clinicalresponse")
## The samples represent the following categories:
## 
##    cure failure      nd 
##      40      33      18
## Using a proportion of observed variants, converting the data to binary observations.
## The factor cure has 40 rows.
## The factor failure has 33 rows.
## The factor nd has 18 rows.
## Finished iterating over the chromosomes.
clinical_sets
## A set of variants observed when cross referencing all variants against
## the samples associated with each metadata factor: clinicalresponse.  3
## categories and 927126 variants were observed with 7
## combinations among them.  725 chromosomes/scaffolds were observed with a
## density of variants ranging from 0.000652315720808871 to 0.114678899082569.
density_vec <- clinical_sets[["density"]]
chromosome_idx <- grep(pattern = "LpaL", x = names(density_vec))
density_df <- as.data.frame(density_vec[chromosome_idx])
density_df[["chr"]] <- rownames(density_df)
colnames(density_df) <- c("density_vec", "chr")
var_den_chr <- ggplot(density_df, aes(x = chr, y = density_vec)) +
  ggplot2::geom_col() +
  ggplot2::theme(axis.text = ggplot2::element_text(size = 10, colour = "black"),
                 axis.text.x = ggplot2::element_text(angle = 90, vjust = 0.5))
var_den_chr

pp(file = "figures/variant_density_by_chromosome.pdf")
var_den_chr
dev.off()
## png 
##   2
## oops, forgot to export write_snps...  fixed.
clinical_written <- write_snps(new_snps_sufficient, output_file = "excel/clinical_variants.aln")

4.4.1 Cross reference these variants by gene

```{r}c clinical_genes <- snps_vs_genes(lp_se, clinical_sets, chr_column = exp_chr_col, start_column = exp_start_col, end_column = exp_end_col)

snp_density <- merge(as.data.frame(clinical_genes[[“summary”]]), as.data.frame(rowData(lp_se)), by = “row.names”) snp_density <- snp_density[, c(1, 2, 4, 15)] colnames(snp_density) <- c(“name”, “snps”, “product”, “length”) snp_density[[“product”]] <- tolower(snp_density[[“product”]]) snp_density[[“length”]] <- as.numeric(snp_density[[“length”]]) snp_density[[“density”]] <- as.numeric(snp_density[[“snps”]]) / snp_density[[“length”]] snp_idx <- order(snp_density[[“density”]], decreasing = TRUE) snp_density <- snp_density[snp_idx, ]

removers <- c(“amastin”, “gp63”, “leishmanolysin”) for (r in removers) { drop_idx <- grepl(pattern = r, x = snp_density[[“product”]]) snp_density <- snp_density[!drop_idx, ] } ## Filter these for [A|a]mastin gp63 Leishmanolysin


Let us grab out the number of variants/gene for the cure/fail samples,
merge them into a dataframe, and add that to the gene annotations for
the lp_se datastructure.


``` r
clinical_snps <- snps_intersections(lp_se, clinical_sets, chr_column = exp_chr_col, start_column = exp_start_col, end_column = exp_end_col)

fail_ref_snps <- as.data.frame(clinical_snps[["inters"]][["failure, reference strain"]])
fail_ref_snps <- rbind(fail_ref_snps,
                       as.data.frame(clinical_snps[["inters"]][["failure"]]))
cure_snps <- as.data.frame(clinical_snps[["inters"]][["cure"]])

head(fail_ref_snps)
##                                                     seqnames  start    end
## chr_LPAL13-SCAF000063_pos_2573_ref_C_alt_A LPAL13-SCAF000063   2573   2574
## chr_LPAL13-SCAF000165_pos_363_ref_G_alt_C  LPAL13-SCAF000165    363    364
## chr_LPAL13-SCAF000627_pos_2267_ref_G_alt_T LPAL13-SCAF000627   2267   2268
## chr_LpaL13-01_pos_26758_ref_T_alt_C                LpaL13-01  26758  26759
## chr_LpaL13-03_pos_164108_ref_A_alt_G               LpaL13-03 164108 164109
## chr_LpaL13-03_pos_236263_ref_T_alt_C               LpaL13-03 236263 236264
##                                            width strand
## chr_LPAL13-SCAF000063_pos_2573_ref_C_alt_A     2      +
## chr_LPAL13-SCAF000165_pos_363_ref_G_alt_C      2      +
## chr_LPAL13-SCAF000627_pos_2267_ref_G_alt_T     2      +
## chr_LpaL13-01_pos_26758_ref_T_alt_C            2      +
## chr_LpaL13-03_pos_164108_ref_A_alt_G           2      +
## chr_LpaL13-03_pos_236263_ref_T_alt_C           2      +
head(cure_snps)
##                                                     seqnames  start    end
## chr_LPAL13-SCAF000397_pos_1312_ref_G_alt_A LPAL13-SCAF000397   1312   1313
## chr_LPAL13-SCAF000627_pos_1869_ref_A_alt_G LPAL13-SCAF000627   1869   1870
## chr_LPAL13-SCAF000791_pos_906_ref_C_alt_T  LPAL13-SCAF000791    906    907
## chr_LpaL13-06_pos_2975_ref_T_alt_C                 LpaL13-06   2975   2976
## chr_LpaL13-09_pos_58219_ref_G_alt_A                LpaL13-09  58219  58220
## chr_LpaL13-10_pos_185578_ref_C_alt_T               LpaL13-10 185578 185579
##                                            width strand
## chr_LPAL13-SCAF000397_pos_1312_ref_G_alt_A     2      +
## chr_LPAL13-SCAF000627_pos_1869_ref_A_alt_G     2      +
## chr_LPAL13-SCAF000791_pos_906_ref_C_alt_T      2      +
## chr_LpaL13-06_pos_2975_ref_T_alt_C             2      +
## chr_LpaL13-09_pos_58219_ref_G_alt_A            2      +
## chr_LpaL13-10_pos_185578_ref_C_alt_T           2      +
write.csv(file = "excel/cure_variants.txt", x = rownames(cure_snps))
write.csv(file = "excel/fail_variants.txt", x = rownames(fail_ref_snps))

annot <- rowData(lp_se)
clinical_interest_cure <- as.data.frame(clinical_snps[["gene_summaries"]][["cure"]])
summary(as.factor(clinical_interest_cure[[1]]))
##    0    1    2    3 
## 8729   41    5    3
clinical_interest_fail <- as.data.frame(clinical_snps[["gene_summaries"]][["failure"]])
summary(as.factor(clinical_interest_fail[[1]]))
##    0    1    2    3 
## 8740   35    1    2
clinical_interest <- merge(clinical_interest_cure,
                           clinical_interest_fail,
                           by = "row.names", all = TRUE)

rownames(clinical_interest) <- clinical_interest[["Row.names"]]
clinical_interest[["Row.names"]] <- NULL
colnames(clinical_interest) <- c("cure_snps", "fail_snps")
clinical_annot <- merge(annot, clinical_interest, by = "row.names")
rownames(annot) <- annot[["Row.names"]]
annot[["Row.names"]] <- NULL
dim(annot)
## [1] 8778  111
dim(rowData(lp_se))
## [1] 8778  111
rowData(lp_se) <- annot

5 Zymodeme for new samples

The heatmap produced here should show the variants only for the zymodeme genes.

5.1 Hunt for snp clusters

I am thinking that if we find clusters of locations which are variant, that might provide some PCR testing possibilities.

## Drop the 2.1, 2.4, unknown, and null
pruned_snps <- subset_se(new_snps_sufficient, subset = "condition=='z2.2'|condition=='z2.3'")
new_sets <- get_snp_sets(pruned_snps, factor = "zymodemecategorical")
## The samples represent the following categories:
## 
## z21 z22 z23 z24 
##   0  42  40   0
## Using a proportion of observed variants, converting the data to binary observations.
## Warning in median_by_factor(assay(data), colData(data), fact = fact, fun =
## fun): The level z21 of the factor has no columns.
## The factor z22 has 42 rows.
## The factor z23 has 40 rows.
## Warning in median_by_factor(assay(data), colData(data), fact = fact, fun =
## fun): The level z24 of the factor has no columns.
## Finished iterating over the chromosomes.
summary(new_sets)
##               Length Class      Mode     
## factor          1    -none-     character
## values          2    data.frame list     
## observations    3    data.frame list     
## possibilities   2    -none-     character
## intersections   3    -none-     list     
## chr_data      725    -none-     list     
## set_names       4    -none-     list     
## invert_names    4    -none-     list     
## density       725    -none-     numeric
## 1000000: 2.2
## 0100000: 2.3

summary(new_sets[["intersections"]][["10"]])
##    Length     Class      Mode 
##       799 character character
write.csv(file = "excel/variants_22.csv", x = new_sets[["intersections"]][["10"]])
summary(new_sets[["intersections"]][["01"]])
##    Length     Class      Mode 
##     67068 character character
write.csv(file = "excel/variants_23.csv", x = new_sets[["intersections"]][["01"]])

Thus we see that there are 3,553 variants associated with 2.2 and 81,589 associated with 2.3.

5.1.1 A small function for searching for potential PCR primers

The following function uses the positional data to look for sequential mismatches associated with zymodeme in the hopes that there will be some regions which would provide good potential targets for a PCR-based assay.

sequential_variants <- function(snp_sets, conditions = NULL, minimum = 3, maximum_separation = 3) {
  if (is.null(conditions)) {
    conditions <- 1
  }
  intersection_sets <- snp_sets[["intersections"]]
  intersection_names <- snp_sets[["set_names"]]
  chosen_intersection <- 1
  if (is.numeric(conditions)) {
    chosen_intersection <- conditions
  } else {
    intersection_idx <- intersection_names == conditions
    chosen_intersection <- names(intersection_names)[intersection_idx]
  }

  possible_positions <- intersection_sets[[chosen_intersection]]
  position_table <- data.frame(row.names = possible_positions)
  pat <- "^chr_(.+)_pos_(.+)_ref_.*$"
  position_table[["chr"]] <- gsub(pattern = pat, replacement = "\\1", x = rownames(position_table))
  position_table[["pos"]] <- as.numeric(gsub(pattern = pat, replacement = "\\2", x = rownames(position_table)))
  position_idx <- order(position_table[, "chr"], position_table[, "pos"])
  position_table <- position_table[position_idx, ]
  position_table[["dist"]] <- 0

  last_chr <- ""
  for (r in 1:nrow(position_table)) {
    this_chr <- position_table[r, "chr"]
    if (r == 1) {
      position_table[r, "dist"] <- position_table[r, "pos"]
      last_chr <- this_chr
      next
    }
    if (this_chr == last_chr) {
      position_table[r, "dist"] <- position_table[r, "pos"] - position_table[r - 1, "pos"]
    } else {
      position_table[r, "dist"] <- position_table[r, "pos"]
    }
    last_chr <- this_chr
  }

  ## Working interactively here.

  doubles <- position_table[["dist"]] == 1
  doubles <- position_table[doubles, ]
  write.csv(doubles, "doubles.csv")

  one_away <- position_table[["dist"]] == 2
  one_away <- position_table[one_away, ]
  write.csv(one_away, "one_away.csv")

  two_away <- position_table[["dist"]] == 3
  two_away <- position_table[two_away, ]
  write.csv(two_away, "two_away.csv")

  combined <- rbind(doubles, one_away)
  combined <- rbind(combined, two_away)
  position_idx <- order(combined[, "chr"], combined[, "pos"])
  combined <- combined[position_idx, ]

  this_chr <- ""
  for (r in 1:nrow(combined)) {
    this_chr <- combined[r, "chr"]
    if (r == 1) {
      combined[r, "dist_pair"] <- combined[r, "pos"]
      last_chr <- this_chr
      next
    }
    if (this_chr == last_chr) {
      combined[r, "dist_pair"] <- combined[r, "pos"] - combined[r - 1, "pos"]
    } else {
      combined[r, "dist_pair"] <- combined[r, "pos"]
    }
    last_chr <- this_chr
  }

  dist_pair_maximum <- 1000
  dist_pair_minimum <- 200
  dist_pair_idx <- combined[["dist_pair"]] <= dist_pair_maximum &
    combined[["dist_pair"]] >= dist_pair_minimum
  remaining <- combined[dist_pair_idx, ]
  no_weak_idx <- grepl(pattern = "ref_(G|C)", x = rownames(remaining))
  remaining <- remaining[no_weak_idx, ]

  print(head(table(position_table[["dist"]])))
  sequentials <- position_table[["dist"]] <= maximum_separation
  message("There are ", sum(sequentials), " candidate regions.")

  ## The following can tell me how many runs of each length occurred, that is not quite what I want.
  ## Now use run length encoding to find the set of sequential sequentials!
  rle_result <- rle(sequentials)
  rle_values <- rle_result[["values"]]
  ## The following line is equivalent to just leaving values alone:
  ## true_values <- rle_result[["values"]] == TRUE
  rle_lengths <- rle_result[["lengths"]]
  true_sequentials <- rle_lengths[rle_values]
  rle_idx <- cumsum(rle_lengths)[which(rle_values)]

  position_table[["last_sequential"]] <- 0
  count <- 0
  for (r in rle_idx) {
    count <- count + 1
    position_table[r, "last_sequential"] <- true_sequentials[count]
  }
  message("The maximum sequential set is: ", max(position_table[["last_sequential"]]), ".")

  wanted_idx <- position_table[["last_sequential"]] >= minimum
  wanted <- position_table[wanted_idx, c("chr", "pos")]
  return(wanted)
}

zymo22_sequentials <- sequential_variants(new_sets, conditions = "z22",
                                          minimum = 1, maximum_separation = 2)
dim(zymo22_sequentials)
## 7 candidate regions for zymodeme 2.2 -- thus I am betting that the reference strain is a 2.2
zymo23_sequentials <- sequential_variants(new_sets, conditions = "z23",
                                          minimum = 2, maximum_separation = 2)
dim(zymo23_sequentials)
## In contrast, there are lots (587) of interesting regions for 2.3!

5.1.2 Extract a promising region from the genome

The first 4 candidate regions from my set of remaining: * Chr Pos. Distance * LpaL13-15 238433 448 * LpaL13-18 142844 613 * LpaL13-29 830342 252 * LpaL13-33 1331507 843

Lets define a couple of terms: * Third: Each of the 4 above positions. * Second: Third - Distance * End: Third + PrimerLen * Start: Second - Primerlen

In each instance, these are the last positions, so we want to grab three things:

  • The entire region from End -> Start, this way we can have a quick sanity check.
  • Start -> Second.
  • (Third -> End) <- Reverse complemented
## * LpaL13-15 238433 448
first_candidate_chr <- lp_genome[["LpaL13_15"]]
primer_length <- 22
amplicon_length <- 448
first_candidate_third <- 238433
first_candidate_second <- first_candidate_third - amplicon_length
first_candidate_start <- first_candidate_second - primer_length
first_candidate_end <- first_candidate_third + primer_length
first_candidate_region <- subseq(first_candidate_chr, first_candidate_start, first_candidate_end)
first_candidate_region
first_candidate_5p <- subseq(first_candidate_chr, first_candidate_start, first_candidate_second)
as.character(first_candidate_5p)
first_candidate_3p <- spgs::reverseComplement(subseq(first_candidate_chr, first_candidate_third, first_candidate_end))
first_candidate_3p

## * LpaL13-18 142844 613
second_candidate_chr <- lp_genome[["LpaL13_18"]]
primer_length <- 22
amplicon_length <- 613
second_candidate_third <- 142844
second_candidate_second <- second_candidate_third - amplicon_length
second_candidate_start <- second_candidate_second - primer_length
second_candidate_end <- second_candidate_third + primer_length
second_candidate_region <- subseq(second_candidate_chr, second_candidate_start, second_candidate_end)
second_candidate_region
second_candidate_5p <- subseq(second_candidate_chr, second_candidate_start, second_candidate_second)
as.character(second_candidate_5p)
second_candidate_3p <- spgs::reverseComplement(subseq(second_candidate_chr, second_candidate_third, second_candidate_end))
second_candidate_3p


## * LpaL13-29 830342 252
third_candidate_chr <- lp_genome[["LpaL13_29"]]
primer_length <- 22
amplicon_length <- 252
third_candidate_third <- 830342
third_candidate_second <- third_candidate_third - amplicon_length
third_candidate_start <- third_candidate_second - primer_length
third_candidate_end <- third_candidate_third + primer_length
third_candidate_region <- subseq(third_candidate_chr, third_candidate_start, third_candidate_end)
third_candidate_region
third_candidate_5p <- subseq(third_candidate_chr, third_candidate_start, third_candidate_second)
as.character(third_candidate_5p)
third_candidate_3p <- spgs::reverseComplement(subseq(third_candidate_chr, third_candidate_third, third_candidate_end))
third_candidate_3p
## You are a garbage polypyrimidine tract.
## Which is actually interesting if the mutations mess it up.


## * LpaL13-33 1331507 843
fourth_candidate_chr <- lp_genome[["LpaL13_33"]]
primer_length <- 22
amplicon_length <- 843
fourth_candidate_third <- 1331507
fourth_candidate_second <- fourth_candidate_third - amplicon_length
fourth_candidate_start <- fourth_candidate_second - primer_length
fourth_candidate_end <- fourth_candidate_third + primer_length
fourth_candidate_region <- subseq(fourth_candidate_chr, fourth_candidate_start, fourth_candidate_end)
fourth_candidate_region
fourth_candidate_5p <- subseq(fourth_candidate_chr, fourth_candidate_start, fourth_candidate_second)
as.character(fourth_candidate_5p)
fourth_candidate_3p <- spgs::reverseComplement(subseq(fourth_candidate_chr, fourth_candidate_third, fourth_candidate_end))
fourth_candidate_3p

5.2 Go hunting for Sanger sequencing regions

I made a fun little function which should find regions which have lots of variants associated with a given experimental factor.

pheno <- subset_se(lp_se, subset = "condition=='z2.2'|condition=='z2.3'")
pheno <- subset_se(pheno, subset = "!is.na(colData(pheno)[['bcftable']])")
pheno_snps <- count_snps(pheno, annot_column = "freebayessummary", snp_column="PAIRED")
## Using the snp column: PAIRED from the sample annotations.
##pheno_snps <- sm(count_snps(pheno, annot_column = "bcftable"))

5.3 SNP Density Primers

I cannot run the following block in the container unless/until I copy the gff into it…

fun_stuff <- snp_density_primers(
  pheno_snps,
  bsgenome = "BSGenome.Leishmania.panamensis.MHOMCOL81L13.v53",
  gff = "reference/TriTrypDB-53_LpanamensisMHOMCOL81L13.gff")
drop_scaffolds <- grepl(x = rownames(fun_stuff$favorites), pattern = "SCAF")
favorite_primer_regions <- fun_stuff[["favorites"]][!drop_scaffolds, ]
favorite_primer_regions[["bin"]] <- rownames(favorite_primer_regions)

favorite_primer_regions <- favorite_primer_regions %>%
  relocate(bin)

5.4 Combine this table with 2.2/2.3 genes

Here is my note from our meeting:

Cross reference primers to DE genes of 2.2/2.3 and/or resistance/suscpetible, add a column to the primer spreadsheet with the DE genes (in retrospect I am guessing this actually means to put the logFC as a column.

One nice thing, I did a semantic removal on the lp_se, so the set of logFC/pvalues should not have any of the offending types; thus I should be able to automagically get rid of them in the merge.

This block needs to go after differential expression analyses.

logfc <- zy_table_sva[["data"]][["z23_vs_z22"]]
logfc_columns <- logfc[, c("deseq_logfc", "deseq_adjp")]
colnames(logfc_columns) <- c("z23_logfc", "z23_adjp")
new_table <- merge(favorite_primer_regions, logfc_columns,
                   by.x = "closest_gene_before_id", by.y = "row.names")
sus <- sus_table_sva[["data"]][["sensitive_vs_resistant"]]
sus_columns <- sus[, c("deseq_logfc", "deseq_adjp")]
colnames(sus_columns) <- c("sus_logfc", "sus_adjp")
new_table <- merge(new_table, sus_columns,
                   by.x = "closest_gene_before_id", by.y = "row.names") %>%
  relocate(bin)
written <- write_xlsx(data = new_table,
                      excel = "excel/favorite_primers_xref_zy_sus.xlsx")

5.5 Make a heatmap describing the clustering of variants

We can cross reference the variants against the zymodeme status and plot a heatmap of the results and hopefully see how they separate.

snp_genes <- sm(snps_vs_genes(lp_se, new_sets, chr_column = exp_chr_col,
                              start_column = exp_start_col, end_column = exp_end_col))

clinical_colors_v2 <- list(
  "z22" = "#0000cc",
  "z23" = "#cc0000")
new_zymo_norm <- normalize_se(pruned_snps, norm = "quant") %>%
  set_conditions(fact = "zymodemecategorical", colors = clinical_colors_v2)
## The numbers of samples by condition are:
## 
## z22 z23 
##  42  40
#  set_se_colors(clinical_colors_v2)

zymo_heat <- plot_disheat(new_zymo_norm)
dev <- pp(file = "images/onlyz22_z23_snp_heatmap.pdf", width = 12, height = 12)
zymo_heat[["plot"]]
closed <- dev.off()
zymo_heat
## A heatmap of pairwise sample distances ranging from: 
## 405123.161006473 to 2192192.81018837.

5.5.1 Annotated heatmap of variants

Now let us try to make a heatmap which includes some of the annotation data.

des <- colData(both_norm)
undef_idx <- is.na(des[["pathogenstrain"]])
des[undef_idx, "pathogenstrain"] <- "unknown"

##hmcols <- colorRampPalette(c("yellow","black","darkblue"))(256)
correlations <- hpgl_cor(assay(both_norm))
na_idx <- is.na(correlations)
correlations[na_idx] <- 0

## Make an initial heatmap via plot_disheat, which may get used as the figure:
initial_snps <- set_conditions(both_norm, fact = "zymodemereference", colors = color_choices[["strain"]])
## The numbers of samples by condition are:
## 
## z2.1 z2.2 z2.3 z2.4 
##    7   42   40    2
## Warning in set_se_colors(new_se, colors = colors): Colors for the following
## categories are not being used: z2.0, z3.0, z3.2, z1.0, z1.5, b2904, unknown.
initial_disheat <- plot_disheat(both_norm)
dev <- pp(file = "figures/initial_snp_heatmap.pdf", width = 20, height = 20)
initial_disheat[["plot"]]
closed <- dev.off()
zymo_heat
## A heatmap of pairwise sample distances ranging from: 
## 405123.161006473 to 2192192.81018837.

zymo_missing_idx <- is.na(des[["zymodemecategorical"]])
des[["zymodemecategorical"]] <- as.character(des[["zymodemecategorical"]])
des[["clinicalcategorical"]] <- as.character(des[["clinicalcategorical"]])
des[zymo_missing_idx, "zymodemecategorical"] <- "unknown"
mydendro <- list(
  "clustfun" = hclust,
  "lwd" = 2.0)
col_data <- as.data.frame(des[, c("zymodemecategorical")])
unknown_clinical <- is.na(des[["clinicalcategorical"]])
colnames(col_data) <- c("zymodeme")

row_data <- as.data.frame(des[, c("sus_category_current", "clinicalcategorical")])
colnames(row_data) <- c("susceptibility", "outcome")
row_data[unknown_clinical, "outcome"] <- "undefined"

myannot <- list(
  "Col" = list("data" = col_data),
  "Row" = list("data" = row_data))
myclust <- list("cuth" = 1.0,
                "col" = BrewerClusterCol)
mylabs <- list(
  "Row" = list("nrow" = 4),
  "Col" = list("nrow" = 4))
hmcols <- colorRampPalette(c("darkblue", "beige"))(240)
zymo_annot_heat <- annHeatmap2(
  correlations,
  dendrogram = mydendro,
  annotation = myannot,
  cluster = myclust,
  labels = mylabs,
  ## The following controls if the picture is symmetric
  scale = "none",
  col = hmcols)
## Warning in breakColors(breaks, col): more colors than classes: ignoring 35 last
## colors
dev <- pp(file = "images/dendro_heatmap.pdf", height = 20, width = 20)
plot(zymo_annot_heat)
closed <- dev.off()
plot(zymo_annot_heat)

Print the larger heatmap so that all the labels appear. Keep in mind that as we get more samples, this image needs to continue getting bigger.

5.5.2 CMplot karyogram of variants

I cannot run the following block until/unless I install cmplot in the container. Oh, I did! Let us run it and see what happens.

xref_prop <- table(colData(pheno_snps)[["condition"]])
xref_prop
## 
## z2.2 z2.3 
##   29   27
idx_tbl <- assay(pheno_snps) > 5
new_tbl <- data.frame(row.names = rownames(assay(pheno_snps)))
for (n in names(xref_prop)) {
  samples <- colData(pheno_snps)[["condition"]] == n
  new_tbl[[n]] <- 0
  prop_col <- rowSums(idx_tbl[, samples]) / xref_prop[n]
  new_tbl[n] <- prop_col
}
keepers <- grepl(x = rownames(new_tbl), pattern = "LpaL13")
new_tbl <- new_tbl[keepers, ]
new_tbl[["strong22"]] <- 1.001 - new_tbl[["z2.2"]]
new_tbl[["strong23"]] <- 1.001 - new_tbl[["z2.3"]]
s22_na <- new_tbl[["strong22"]] > 1
new_tbl[s22_na, "strong22"] <- 1
s23_na <- new_tbl[["strong23"]] > 1
new_tbl[s23_na, "strong23"] <- 1

new_tbl[["SNP"]] <- rownames(new_tbl)
new_tbl[["Chromosome"]] <- gsub(x = new_tbl[["SNP"]], pattern = "chr_(.*)_pos_.*", replacement = "\\1")
new_tbl[["Position"]] <- gsub(x = new_tbl[["SNP"]], pattern = ".*_pos_(\\d+)_.*", replacement = "\\1")
new_tbl <- new_tbl[, c("SNP", "Chromosome", "Position", "strong22", "strong23")]

simplify <- new_tbl
simplify[["strong22"]] <- NULL

CMplot(new_tbl, bin.size = 10000, threshold = c(0.01, 0.05), plot.type = "d",
       file.name = "variant_density_10k")
##  Marker density plotting.
##  Plots are stored in: /lab/singularity/clinical_strains_analyses/202604031644_outputs
CMplot(new_tbl, bin.size = 1000, threshold = c(0.01, 0.05), plot.type = "d",
       file.name = "variant_density_1k")
##  Marker density plotting.
##  Plots are stored in: /lab/singularity/clinical_strains_analyses/202604031644_outputs
CMplot(new_tbl, bin.size = 100000, threshold = c(0.01, 0.05), plot.type = "d",
       file.name = "variant_density_100k")
##  Marker density plotting.
##  Plots are stored in: /lab/singularity/clinical_strains_analyses/202604031644_outputs
CMplot(new_tbl, plot.type = "m", multracks = TRUE, threshold = c(0.01, 0.05),
       threshold.lwd = c(1,1), threshold.col = c("black","grey"),
       amplify = TRUE, bin.size = 1000,
       chr.den.col = c("darkgreen", "yellow", "red"),
       signal.col = c("red", "green", "blue"),
       signal.cex = 1, file = "jpg", dpi = 300, file.output = TRUE, verbose = TRUE)
##  Multi-tracks Manhattan plotting strong22.
##  Multi-tracks Manhattan plotting strong23.
##  Plots are stored in: /lab/singularity/clinical_strains_analyses/202604031644_outputs
SNP Density
SNP Density

6 A different karyogram

I have been a bit frustrated with the clunkyness of cmplot, so I did some reading and found autoplot. It makes use of g/iranges to plot arbitrary data and as such has the potential to be significantly more generally useful than cmplot. I think I will be able to use it to view a lot of interesting different data types. In this instance I want to plot density of variants associated with various conditions in the data (z2.3/z2.2, cure/fail, whatever). In addition, it might be nice to have the ORFs displayed in some fashion (space permitting).

I am pretty sure I made a function which makes this less clunky than what follows.

lp_entry <- EuPathDB::get_eupath_entry(species = "MHOM/COL", metadata = eu_meta)

## These lines cannot run in the container because it cannot write
##txdb_pkgname <- make_eupath_txdb(lp_entry)
##grange_name <- make_eupath_granges(lp_entry)
grange_name <- gsub(x = lp_entry[["GrangesPkg"]], pattern = "\\.rda$", replacement = "")
grange_filename <- file.path("build", lp_entry[["GrangesPkg"]])
if (file.exists(grange_filename)) {
  load(grange_filename)
} else {
  created <- dir.create("build/gff", recursive = TRUE)
  grange_build <- make_eupath_granges(lp_entry)
  grange_filename <- grange_build[["rda"]]
  load(grange_filename)
}
grange_data <- get0(grange_name)

scaffold_idx <- grepl(x = as.character(seqnames(grange_data)), pattern = "SCAF")
no_scaffolds <- grange_data[!scaffold_idx]
scaffold_idx <- grepl(x = as.character(names(seqinfo(grange_data))), pattern = "SCAF")
chr_names <- names(seqinfo(grange_data))[!scaffold_idx]
no_scaffolds <- seqinfo(grange_data)[chr_names]

auto_tbl <- new_tbl
auto_tbl[["position2"]] <- auto_tbl[["Position"]]
auto_tbl[["SNP"]] <- NULL
rownames(auto_tbl) <- NULL

tilesize <- 1000
bins_1k <- GenomicRanges::tileGenome(seqlengths(no_scaffolds), tilewidth = 1000,
                                     cut.last.tile.in.chrom = TRUE)
bins_5k <- GenomicRanges::tileGenome(seqlengths(no_scaffolds), tilewidth = 5000,
                                     cut.last.tile.in.chrom = TRUE)
bins_10k <- GenomicRanges::tileGenome(seqlengths(no_scaffolds), tilewidth = 10000,
                                  cut.last.tile.in.chrom = TRUE)
bins_1nt <- GenomicRanges::tileGenome(seqlengths(no_scaffolds), tilewidth = 1,
                                      cut.last.tile.in.chrom = TRUE)
auto_tbl[["strand"]] <- "+"
## I want to calculate the number of intersecting positions between my auto_tbl and the 1k bins.
start <- auto_tbl[, c("Chromosome", "Position", "position2", "strand", "strong23")]
colnames(start) <- c("chr", "start", "end", "strand", "z23")
start[["chr"]] <- gsub(x = start[["chr"]], pattern = "-", replacement = "_")
var_grange <- makeGRangesFromDataFrame(start, seqinfo = no_scaffolds, keep.extra.columns = TRUE)
vars_per_bin <- findOverlaps(bins_1k, var_grange)
vars_per_bin_numeric <- as.data.frame(bins_1k)
vars_per_bin_numeric[["bin"]] <- rownames(vars_per_bin_numeric)

count_per_bin <- as.data.frame(vars_per_bin) %>%
  group_by(queryHits) %>%
  dplyr::tally()
colnames(count_per_bin) <- c("bin", "num")
vars_per_bin_numeric <- merge(vars_per_bin_numeric, count_per_bin, by = "bin", all.x = TRUE)
missing_idx <- is.na(vars_per_bin_numeric[["num"]])
vars_per_bin_numeric[missing_idx, "num"] <- 0
vars_per_bin <- vars_per_bin_numeric[, c("seqnames", "start", "end", "width", "strand", "num")]
vpb_grange <- makeGRangesFromDataFrame(vars_per_bin, seqinfo = no_scaffolds, keep.extra.columns = TRUE)

kary <- autoplot(vpb_grange, layout = "karyogram", aes(color = num, fill = num)) +
  scale_color_gradient(low = "blue", high = "red") +
  scale_fill_gradient(low = "blue", high = "red")
## theme_bw(base_size = 10) +
pp(file = "karyogram_by_variants.pdf", height = 24, width = 18)
kary
dev.off()

var_kary <- ggbio() +
  layout_karyogram(vpb_grange, aes(color = num, fill = num)) +
  scale_fill_gradient(low = "blue", high = "white") +
  scale_color_gradient(low = "blue", high = "white") +
  theme_bw(base_size = 10)
var_kary

6.1 Try out MatrixEQTL

This tool looks a little opaque, but provides sample data with things that make sense to me and should be pretty easy to recapitulate in our data.

  1. covariates.txt: Columns are samples, rows are things from colData – the most likely ones of interest for our data would be zymodeme, sensitivity
  2. geneloc.txt: columns are ‘geneid’, ‘chr’, ‘left’, ‘right’. I guess I can assume left and right are start/stop; in which case this is trivially acquirable from rowData.
  3. ge.txt: This appears to be a log(rpkm/cpm) table with rows as genes and columns as samples
  4. snpsloc.txt: columns are ‘snpid’, ‘chr’, ‘pos’
  5. snps.txt: columns are samples, rows are the ids from snsploc, values a 0,1,2. I assume 0 is identical and 1..12 are the various A->TGC T->AGC C->AGT G->ACT
## For this, let us use the 'new_snps' data structure.
## Caveat here: these need to be coerced to numbers.
my_covariates <- colData(new_snps)[, c("zymodemecategorical", "clinicalcategorical")]
for (col in colnames(my_covariates)) {
  my_covariates[[col]] <- as.numeric(as.factor(my_covariates[[col]]))
}
my_covariates <- t(my_covariates)

my_geneloc <- rowData(lp_se)[, c("gid", "chromosome", "start", "end")]
colnames(my_geneloc) <- c("geneid", "chr", "left", "right")

my_ge <- assay(normalize_se(lp_se, transform = "log2", filter = TRUE, convert = "cpm"))
used_samples <- tolower(colnames(my_ge)) %in% colnames(assay(new_snps))
my_ge <- my_ge[, used_samples]

my_snpsloc <- data.frame(rownames = rownames(assay(new_snps)))
## Oh, caveat here: Because of the way I stored the data,
## I could have duplicate rows which presumably will make matrixEQTL sad
my_snpsloc[["chr"]] <- gsub(pattern = "^chr_(.+)_pos(.+)_ref_.*$", replacement = "\\1",
                            x = rownames(my_snpsloc))
my_snpsloc[["pos"]] <- gsub(pattern = "^chr_(.+)_pos(.+)_ref_.*$", replacement = "\\2",
                            x = rownames(my_snpsloc))
test <- duplicated(my_snpsloc)
## Each duplicated row would be another variant at that position;
## so in theory we would do a rle to number them I am guessing
## However, I do not have different variants so I think I can ignore this for the moment
## but will need to make my matrix either 0 or 1.
if (sum(test) > 0) {
  message("There are: ", sum(duplicated), " duplicated entries.")
  keep_idx <- ! test
  my_snpsloc <- my_snpsloc[keep_idx, ]
}

my_snps <- assay(new_snps)
one_idx <- my_snps > 0
my_snps[one_idx] <- 1

## Ok, at this point I think I have all the pieces which this method wants...
## Oh, no I guess not; it actually wants the data as a set of filenames...
library(MatrixEQTL)
write.table(my_snps, "eqtl/snps.tsv", na = "NA", col.names = TRUE, row.names = TRUE, sep = "\t", quote = TRUE)
## readr::write_tsv(my_snps, "eqtl/snps.tsv", )
write.table(my_snpsloc, "eqtl/snpsloc.tsv", na = "NA", col.names = TRUE, row.names = TRUE, sep = "\t", quote = TRUE)
## readr::write_tsv(my_snpsloc, "eqtl/snpsloc.tsv")
write.table(as.data.frame(my_ge), "eqtl/ge.tsv", na = "NA", col.names = TRUE, row.names = TRUE, sep = "\t", quote = TRUE)
## readr::write_tsv(as.data.frame(my_ge), "eqtl/ge.tsv")
write.table(as.data.frame(my_geneloc), "eqtl/geneloc.tsv", na = "NA", col.names = TRUE, row.names = TRUE, sep = "\t", quote = TRUE)
## readr::write_tsv(as.data.frame(my_geneloc), "eqtl/geneloc.tsv")
write.table(as.data.frame(my_covariates), "eqtl/covariates.tsv", na = "NA", col.names = TRUE, row.names = TRUE, sep = "\t", quote = TRUE)
## readr::write_tsv(as.data.frame(my_covariates), "eqtl/covariates.tsv")

useModel = modelLINEAR # modelANOVA, modelLINEAR, or modelLINEAR_CROSS

# Genotype file name
SNP_file_name = "eqtl/snps.tsv"
snps_location_file_name = "eqtl/snpsloc.tsv"
expression_file_name = "eqtl/ge.tsv"
gene_location_file_name = "eqtl/geneloc.tsv"
covariates_file_name = "eqtl/covariates.tsv"
# Output file name
output_file_name_cis = tempfile()
output_file_name_tra = tempfile()
# Only associations significant at this level will be saved
pvOutputThreshold_cis = 0.1
pvOutputThreshold_tra = 0.1
# Error covariance matrix
# Set to numeric() for identity.
errorCovariance = numeric()
# errorCovariance = read.table("Sample_Data/errorCovariance.txt");
# Distance for local gene-SNP pairs
cisDist = 1e6
## Load genotype data
snps = SlicedData$new()
snps$fileDelimiter = "\t"      # the TAB character
snps$fileOmitCharacters = "NA" # denote missing values;
snps$fileSkipRows = 1          # one row of column labels
snps$fileSkipColumns = 1       # one column of row labels
snps$fileSliceSize = 2000      # read file in slices of 2,000 rows
snps$LoadFile(SNP_file_name)
## Load gene expression data
gene = SlicedData$new()
gene$fileDelimiter = "\t"      # the TAB character
gene$fileOmitCharacters = "NA" # denote missing values;
gene$fileSkipRows = 1          # one row of column labels
gene$fileSkipColumns = 1       # one column of row labels
gene$fileSliceSize = 2000      # read file in slices of 2,000 rows
gene$LoadFile(expression_file_name)
## Load covariates
cvrt = SlicedData$new()
cvrt$fileDelimiter = "\t"      # the TAB character
cvrt$fileOmitCharacters = "NA" # denote missing values;
cvrt$fileSkipRows = 1          # one row of column labels
cvrt$fileSkipColumns = 1       # one column of row labels
if(length(covariates_file_name) > 0) {
  cvrt$LoadFile(covariates_file_name)
}
## Run the analysis
snpspos = read.table(snps_location_file_name, header = TRUE, stringsAsFactors = FALSE)
genepos = read.table(gene_location_file_name, header = TRUE, stringsAsFactors = FALSE)

me = Matrix_eQTL_main(
  snps = snps,
  gene = gene,
  cvrt = cvrt,
  output_file_name = output_file_name_tra,
  pvOutputThreshold = pvOutputThreshold_tra,
  useModel = useModel,
  errorCovariance = errorCovariance,
  verbose = TRUE,
  output_file_name.cis = output_file_name_cis,
  pvOutputThreshold.cis = pvOutputThreshold_cis,
  snpspos = snpspos,
  genepos = genepos,
  cisDist = cisDist,
  pvalue.hist = "qqplot",
  min.pv.by.genesnp = FALSE,
  noFDRsaveMemory = FALSE);
pander::pander(sessionInfo())
## Warning: Your system is mis-configured: '/etc/localtime' is not a symlink
## Warning: It is strongly recommended to set envionment variable TZ to
## 'America/New_York' (or equivalent)

R version 4.5.0 (2025-04-11)

Platform: x86_64-pc-linux-gnu

locale: C

attached base packages: stats4, stats, graphics, grDevices, utils, datasets, methods and base

other attached packages: foreach(v.1.5.2), edgeR(v.4.8.2), ruv(v.0.9.7.1), hpgltools(v.2026.03), Heatplus(v.3.18.0), glue(v.1.8.0), ggbio(v.1.58.0), ggplot2(v.4.0.2), GenomicRanges(v.1.62.1), Seqinfo(v.1.0.0), IRanges(v.2.44.0), S4Vectors(v.0.48.0), BiocGenerics(v.0.56.0), generics(v.0.1.4), dplyr(v.1.2.0) and CMplot(v.4.5.1)

loaded via a namespace (and not attached): fs(v.2.0.1), ProtGenerics(v.1.42.0), matrixStats(v.1.5.0), bitops(v.1.0-9), blockmodeling(v.1.1.8), doParallel(v.1.0.17), httr(v.1.4.8), RColorBrewer(v.1.1-3), numDeriv(v.2016.8-1.1), tools(v.4.5.0), backports(v.1.5.0), R6(v.2.6.1), lazyeval(v.0.2.2), mgcv(v.1.9-3), withr(v.3.0.2), gridExtra(v.2.3), preprocessCore(v.1.70.0), cli(v.3.6.5), Biobase(v.2.70.0), labeling(v.0.4.3), EBSeq(v.2.8.0), sass(v.0.4.10), robustbase(v.0.99-7), mvtnorm(v.1.3-6), S7(v.0.2.1), genefilter(v.1.92.0), Rsamtools(v.2.26.0), yulab.utils(v.0.2.4), foreign(v.0.8-90), DOSE(v.4.4.0), R.utils(v.2.13.0), dichromat(v.2.0-0.1), BSgenome(v.1.78.0), limma(v.3.66.0), rstudioapi(v.0.18.0), RSQLite(v.2.4.6), BiocIO(v.1.20.0), gtools(v.3.9.5), crosstalk(v.1.2.2), zip(v.2.3.3), GO.db(v.3.22.0), Matrix(v.1.7-3), abind(v.1.4-8), R.methodsS3(v.1.8.2), lifecycle(v.1.0.5), yaml(v.2.3.12), SummarizedExperiment(v.1.40.0), gplots(v.3.3.0), qvalue(v.2.42.0), SparseArray(v.1.10.10), Rtsne(v.0.17), grid(v.4.5.0), blob(v.1.3.0), promises(v.1.5.0), crayon(v.1.5.3), lattice(v.0.22-7), cowplot(v.1.2.0), GenomicFeatures(v.1.62.0), cigarillo(v.1.0.0), annotate(v.1.88.0), KEGGREST(v.1.50.0), pillar(v.1.11.1), knitr(v.1.51), varhandle(v.2.0.6), fgsea(v.1.36.2), rjson(v.0.2.23), boot(v.1.3-31), corpcor(v.1.6.10), codetools(v.0.2-20), fastmatch(v.1.1-8), Vennerable(v.3.1.0.9000), data.table(v.1.18.2.1), vctrs(v.0.7.2), png(v.0.1-9), Rdpack(v.2.6.6), testthat(v.3.3.2), gtable(v.0.3.6), cachem(v.1.1.0), openxlsx(v.4.2.8.1), xfun(v.0.57), rbibutils(v.2.4.1), S4Arrays(v.1.10.1), mime(v.0.13), RcppEigen(v.0.3.4.0.2), reformulas(v.0.4.4), survival(v.3.8-3), NOISeq(v.2.54.0), iterators(v.1.0.14), statmod(v.1.5.1), nlme(v.3.1-168), pbkrtest(v.0.5.5), bit64(v.4.6.0-1), EnvStats(v.3.1.0), rprojroot(v.2.1.1), GenomeInfoDb(v.1.46.2), bslib(v.0.10.0), KernSmooth(v.2.23-26), otel(v.0.2.0), rpart(v.4.1.24), colorspace(v.2.1-2), DBI(v.1.3.0), Hmisc(v.5.2-5), nnet(v.7.3-20), DESeq2(v.1.50.2), tidyselect(v.1.2.1), bit(v.4.6.0), compiler(v.4.5.0), curl(v.7.0.0), graph(v.1.88.1), htmlTable(v.2.4.3), desc(v.1.4.3), DelayedArray(v.0.36.1), plotly(v.4.12.0), rtracklayer(v.1.70.1), checkmate(v.2.3.4), scales(v.1.4.0), caTools(v.1.18.3), DEoptimR(v.1.1-4), remaCor(v.0.0.20), RBGL(v.1.86.0), rappdirs(v.0.3.4), stringr(v.1.6.0), digest(v.0.6.39), minqa(v.1.2.8), variancePartition(v.1.40.2), rmarkdown(v.2.31), aod(v.1.3.3), XVector(v.0.50.0), RhpcBLASctl(v.0.23-42), htmltools(v.0.5.9), pkgconfig(v.2.0.3), base64enc(v.0.1-6), lme4(v.2.0-1), MatrixGenerics(v.1.22.0), fastmap(v.1.2.0), ensembldb(v.2.34.0), rlang(v.1.1.7), htmlwidgets(v.1.6.4), UCSC.utils(v.1.6.1), shiny(v.1.13.0), farver(v.2.1.2), jquerylib(v.0.1.4), jsonlite(v.2.0.0), BiocParallel(v.1.44.0), GOSemSim(v.2.36.0), R.oo(v.1.27.1), VariantAnnotation(v.1.56.0), RCurl(v.1.98-1.18), magrittr(v.2.0.4), Formula(v.1.2-5), Rcpp(v.1.1.1), stringi(v.1.8.7), brio(v.1.1.5), MASS(v.7.3-65), plyr(v.1.8.9), parallel(v.4.5.0), ggrepel(v.0.9.8), doSNOW(v.1.0.20), Biostrings(v.2.78.0), splines(v.4.5.0), pander(v.0.6.6), locfit(v.1.5-9.12), fastcluster(v.1.3.0), reshape2(v.1.4.5), restez(v.2.1.5), pkgload(v.1.5.1), XML(v.3.99-0.23), evaluate(v.1.0.5), biovizBase(v.1.58.0), BiocManager(v.1.30.27), nloptr(v.2.2.1), httpuv(v.1.6.17), tidyr(v.1.3.2), purrr(v.1.2.1), broom(v.1.0.12), xtable(v.1.8-8), restfulr(v.0.0.16), AnnotationFilter(v.1.34.0), fANCOVA(v.0.6-1), later(v.1.4.8), viridisLite(v.0.4.3), snow(v.0.4-4), OrganismDbi(v.1.52.0), tibble(v.3.3.1), lmerTest(v.3.2-1), memoise(v.2.0.1), AnnotationDbi(v.1.72.0), GenomicAlignments(v.1.46.0), cluster(v.2.1.8.1) and sva(v.3.58.0)

message(paste0("This is hpgltools commit: ", get_git_commit()))
## If you wish to reproduce this exact build of hpgltools, invoke the following:
## > git clone http://github.com/abelew/hpgltools.git
## > git reset 1e2cdb5ddcda51c4ec0bee7383d2e8a73ce98b02
## This is hpgltools commit: Fri Apr 3 16:26:02 2026 -0400: 1e2cdb5ddcda51c4ec0bee7383d2e8a73ce98b02
##message(paste0("Saving to ", savefile))
## tmp <- sm(saveme(filename = savefile))
tmp <- loame(filename = savefile)
LS0tCnRpdGxlOiAiVE1SQzIgYHIgU3lzLmdldGVudignVkVSU0lPTicpYDogVmlzdWFsaXppbmcgQW5hbHlzZXMgYmVmb3JlIERFL3ZhcmlhbnQgYW5hbHlzZXMiCmF1dGhvcjogImF0YiBhYmVsZXdAZ21haWwuY29tIgpkYXRlOiAiYHIgU3lzLkRhdGUoKWAiCm91dHB1dDoKICBodG1sX2RvY3VtZW50OgogICAgY29kZV9kb3dubG9hZDogdHJ1ZQogICAgY29kZV9mb2xkaW5nOiBzaG93CiAgICBmaWdfY2FwdGlvbjogdHJ1ZQogICAgZmlnX2hlaWdodDogOQogICAgZmlnX3dpZHRoOiA5CiAgICBoaWdobGlnaHQ6IHplbmJ1cm4KICAgIGtlZXBfbWQ6IGZhbHNlCiAgICBtb2RlOiBzZWxmY29udGFpbmVkCiAgICBudW1iZXJfc2VjdGlvbnM6IHRydWUKICAgIHNlbGZfY29udGFpbmVkOiB0cnVlCiAgICB0aGVtZTogcmVhZGFibGUKICAgIHRvYzogdHJ1ZQogICAgdG9jX2Zsb2F0OgogICAgICBjb2xsYXBzZWQ6IGZhbHNlCiAgICAgIHNtb290aF9zY3JvbGw6IGZhbHNlCi0tLQoKPHN0eWxlIHR5cGU9InRleHQvY3NzIj4KYm9keSAubWFpbi1jb250YWluZXIgewogIG1heC13aWR0aDogMTYwMHB4Owp9CmJvZHksIHRkIHsKICBmb250LXNpemU6IDE2cHg7Cn0KY29kZS5yewogIGZvbnQtc2l6ZTogMTZweDsKfQpwcmUgewogIGZvbnQtc2l6ZTogMTZweAp9Cjwvc3R5bGU+CgpgYGB7ciwgaW5jbHVkZSA9IEZBTFNFfQpsaWJyYXJ5KENNcGxvdCkKbGlicmFyeShkcGx5cikKbGlicmFyeShHZW5vbWljUmFuZ2VzKQpsaWJyYXJ5KGdnYmlvKQpsaWJyYXJ5KGdncGxvdDIpCmxpYnJhcnkoZ2x1ZSkKbGlicmFyeShIZWF0cGx1cykKbGlicmFyeShocGdsdG9vbHMpCgprbml0cjo6b3B0c19rbml0JHNldChwcm9ncmVzcyA9IFRSVUUsIHZlcmJvc2UgPSBUUlVFLCB3aWR0aCA9IDkwLCBlY2hvID0gVFJVRSkKa25pdHI6Om9wdHNfY2h1bmskc2V0KAogIGVycm9yID0gVFJVRSwgZmlnLndpZHRoID0gOSwgZmlnLmhlaWdodCA9IDksIGZpZy5yZXRpbmEgPSAyLAogIG91dC53aWR0aCA9ICIxMDAlIiwgZGV2ID0gInBuZyIsCiAgZGV2LmFyZ3MgPSBsaXN0KHBuZyA9IGxpc3QodHlwZSA9ICJjYWlyby1wbmciKSkpCm9sZF9vcHRpb25zIDwtIG9wdGlvbnMoZGlnaXRzID0gNCwgc3RyaW5nc0FzRmFjdG9ycyA9IEZBTFNFLCBrbml0ci5kdXBsaWNhdGUubGFiZWwgPSAiYWxsb3ciKQpnZ3Bsb3QyOjp0aGVtZV9zZXQoZ2dwbG90Mjo6dGhlbWVfYncoYmFzZV9zaXplID0gMTIpKQp2ZXIgPC0gU3lzLmdldGVudigiVkVSU0lPTiIpCnByZXZpb3VzX2ZpbGUgPC0gIiIKcnVuZGF0ZSA8LSBmb3JtYXQoU3lzLkRhdGUoKSwgZm9ybWF0ID0gIiVZJW0lZCIpCgojIyB0bXAgPC0gdHJ5KHNtKGxvYWRtZShmaWxlbmFtZSA9IGdzdWIocGF0dGVybiA9ICJcXC5SbWQiLCByZXBsYWNlID0gIlxcLnJkYVxcLnh6IiwgeCA9IHByZXZpb3VzX2ZpbGUpKSkpCnJtZF9maWxlIDwtICIwMnByZV92aXN1YWxpemF0aW9uLlJtZCIKc2F2ZWZpbGUgPC0gZ3N1YihwYXR0ZXJuID0gIlxcLlJtZCIsIHJlcGxhY2UgPSAiXFwucmRhXFwueHoiLCB4ID0gcm1kX2ZpbGUpCmxvYWRlZCA8LSBsb2FkKGZpbGUgPSBnbHVlKCJyZGEvdG1yYzJfZGF0YV9zdHJ1Y3R1cmVzLXZ7dmVyfS5yZGEiKSkKYGBgCgpCZWZvcmUgd2UgYmVnaW4sIGEgY291cGxlIG9mIHBhcmFtZXRlcnMgd2hpY2ggaGF2ZSBnaXZlbiBtZSBncmllZi4KCmBgYHtyfQojIyBVc2VkIGJ5IHRoZSB2YXJpb3VzIGZ1bmN0aW9ucyB3aGljaCBjcm9zcyByZWZlcmVuY2UgZ3JhbmdlIGRhdGEKIyMgVGhlIFNFcyB1c2VkIGluIHRoaXMgZG9jdW1lbnQgYXJlIGdldHRpbmcgdGhpcyBmcm9tIHRoZSBvcmdkYgojIyB3aGljaCBpbmNsdWRlcyB0aGlzIGluZm9ybWF0aW9uIGluIG11bHRpcGxlIGNvbHVtbnMgd2l0aCBkaWZmZXJlbnQKIyMgY2hyb21vc29tZSBJRCBwcmVmaXhlcy4gIEUuZy4gc29tZXRpbWVzIGl0IGlzIGp1c3QgMSwyLDMsIC4uLiBvdGhlciB0aW1lcwojIyBpdCBpcyBMcGFMMSwgTHBhTDIsIExwYUwzLCAuLi4KZXhwX2Nocl9jb2wgPC0gInNlcXVlbmNlX2lkIgojIyBUaGUgdHJpdHJ5cGRiIGFsc28gcHV0cyB0aGUgc3RhcnQvc3RvcC9zdHJhbmQgaW5mb3JtYXRpb24gaW4gbXVsdGlwbGUgcGxhY2VzCmV4cF9zdGFydF9jb2wgPC0gImNvZGluZ19zdGFydCIKZXhwX2VuZF9jb2wgPC0gImNvZGluZ19lbmQiCmBgYAoKIyBJbnRyb2R1Y3Rpb24KClRoaXMgZG9jdW1lbnQgd2lsbCB2aXN1YWxpemUgdGhlIFRNUkMyIHNhbXBsZXMgYmVmb3JlIGNvbXBsZXRpbmcgdGhlIHZhcmlvdXMgZGlmZmVyZW50aWFsCmV4cHJlc3Npb24gYW5kIHZhcmlhbnQgYW5hbHlzZXMgaW4gdGhlIGhvcGVzIG9mIGdldHRpbmcgYW4gdW5kZXJzdGFuZGluZyBvZiBob3cgdGhlIHZhcmlvdXMKc2FtcGxlcyByZWxhdGUgdG8gZWFjaCBvdGhlci4KCiMjIEluaXRpYWwgbGlicmFyeSBzaXplCgpTdGFydCBvZmYgd2l0aCB0aGUgbGlicmFyeSBzaXplcyBvZiB0aGUgb3JpZ2luYWwgZGF0YXNldC4gIFRoZSBtYWluCnRoaW5nIHRvIG5vdGUgaXMgdGhhdCB3ZSBoYXZlIHF1aXRlIGEgbGFyZ2UgdmFyaWFuY2UgaW4gY292ZXJhZ2UuICBBCmZldyBvZiB0aGVzZSBzYW1wbGVzIGFyZSBoaWdobHkgbGlrZWx5IHRvIGJlIHJlbW92ZWQgc2hvcnRseSAobG9va2luZwphdCB5b3UsIFRNUkMyMDAwMSBhbmQgVE1SQzIwMDk1KQoKYGBge3J9CmxpYnNpemVzIDwtIHBsb3RfbGlic2l6ZShscF9zZSkKbGlic2l6ZXMKZGV2IDwtIHBwKCJpbWFnZXMvbHBfc2VfbGlic2l6ZXMucG5nIiwgd2lkdGggPSAxOCwgaGVpZ2h0ID0gOSkKbGlic2l6ZXMkcGxvdApjbG9zZWQgPC0gZGV2Lm9mZigpCmBgYAoKTGlicmFyeSBzaXplcyBvZiB0aGUgcHJvdGVpbiBjb2RpbmcgZ2VuZSBjb3VudHMgb2JzZXJ2ZWQgcGVyIHNhbXBsZS4KVGhlIHNhbXBsZXMgd2VyZSBtYXBwZWQgd2l0aCB0aGUgRXVQYXRoREIgcmV2aXNpb24gMzYgb2YgdGhlCkxlaXNobWFuaWEgKFZpYW5uaWEpIHBhbmFtZW5zaXMgc3RyYWluIE1IT00vQ09MLzgxTDEzIGdlbm9tZTsgdGhlCmFsaWdubWVudHMgd2VyZSBzb3J0ZWQsIGluZGV4ZWQsIGFuZCBjb3VudGVkIHZpYSBodHNlcSB1c2luZyB0aGUgZ2VuZQpmZWF0dXJlcywgYW5kIG5vbi1wcm90ZWluIGNvZGluZyBmZWF0dXJlcyB3ZXJlIGV4Y2x1ZGVkLgpUaGUgcGVyLXNhbXBsZSBzdW1zIG9mIHRoZSByZW1haW5pbmcgbWF0cml4IHdlcmUgcGxvdHRlZCB0byBjaGVjayB0aGF0CnRoZSByZWxhdGl2ZSBzYW1wbGUgY292ZXJhZ2UgaXMgc3VmZmljaWVudCBhbmQgbm90IHRvbyBkaXZlcmdlbnQKYWNyb3NzIHNhbXBsZXMuICBCYXJzIGFyZSBjb2xvcmVkIGFjY29yZGluZyB0byBzdHJhaW4venltb2RlbWUKYW5ub3RhdGlvbjogcmVkOiB6eW1vZGVtZSAyLjM7IGJsdWU6IHp5bW9kZW1lIDIuMjsgTGVpc2htYW5pYQpicmF6aWxpZW5zaXMtbGlrZSBzdHJhaW5zIGIyOTA0LCB6MS4wLCBhbmQgejEuNTogcHVycGxlOyB6eW1vZGVtZXMKd2hpY2ggYXJlIG1vc3Qgc2ltaWxhciB0byAyLjMsIGNvbXByaXNpbmcgejIuNCBpcyBsaWdodCBicm93bjsKenltb2RlbWVzIG1vc3Qgc2ltaWxhciB0byAyLjIsIGNvbXByaXNpbmcgejMuMCwgejIuMCwgejIuMSwgYW5kIHozLjIKYXJlIGxpZ2h0IGdyYXksIGRhcmsgZ3JheSwgZGFyayBicm93biwgYW5kIGdyYXkgcmVzcGVjdGl2ZWx5LgoKIyMgTm9uLXplcm8gZ2VuZXMgd2l0aCByZXNwZWN0IHRvIGNvdmVyYWdlCgpUaGlzIHBsb3QgaXMgdXN1YWxseSBvdXIgcHJpbWFyeSBhcmJpdGVyIGZvciBzYW1wbGUgcmVtb3ZpbmcgYmFzZWQgb24KY292ZXJhZ2UuICBXZSBwaWNrIGEgc2VtaS1hcmJpdHJhcnkgY3V0b2ZmIGJhc2VkIG9uIGJvdGggY292ZXJhZ2UgYW5kCmdlbmVzIG9ic2VydmVkLiAgSW4gdGhpcyBpbnN0YW5jZSA4LDYwMCBnZW5lcyBzZWVtcyBsaWtlbHk/CgpUaGUgY3V0b2ZmIGFyZ3VtZW50IHByaW50cyBvdXQgc2FtcGxlcyB3aXRoIGdlbmUgY292ZXJhZ2UgPCB0aGF0CnByb3BvcnRpb24uICBJIHRoaW5rIHdlIGFscmVhZHkgZHJvcHBlZCBpbiB0aGUgc2FtcGxlIHNoZWV0IHRoZSBtb3N0CnByb2JsZW1hdGljIHNhbXBsZXMsIHNvIGl0IG1heSBub3QgYWN0dWFsbHkgcHJpbnQgYW55dGhpbmcuCgpgYGB7cn0KIyMgSSB0aGluayBzYW1wbGVzIDcsMTAgc2hvdWxkIGJlIHJlbW92ZWQgYXQgbWluaW11bSwgcHJvYmFibHkgYWxzbyA5LDExCm5vbnplcm8gPC0gcGxvdF9ub256ZXJvKGxwX3NlLCBjdXRvZmYgPSAwLjcsIHlfaW50ZXJjZXB0ID0gMC45OSkKbm9uemVybwpkZXYgPC0gcHAoZmlsZSA9ICJpbWFnZXMvbHBfbm9uemVyby5wbmciLCB3aWR0aCA9IDksIGhlaWdodCA9IDkpCm5vbnplcm8kcGxvdApjbG9zZWQgPC0gZGV2Lm9mZigpCmBgYAoKRGlmZmVyZW5jZXMgaW4gcmVsYXRpdmUgZ2VuZSBjb250ZW50IHdpdGggcmVzcGVjdCB0byBzZXF1ZW5jaW5nCmNvdmVyYWdlLiAgVGhlIHBlci1zYW1wbGUgbnVtYmVyIG9mIG9ic2VydmVkIGdlbmVzIHdhcyBwbG90dGVkIHdpdGgKcmVzcGVjdCB0byB0aGUgcmVsYXRpdmUgQ1BNIGNvdmVyYWdlIGluIG9yZGVyIHRvIGNoZWNrIHRoYXQgdGhlCnNhbXBsZXMgYXJlIHN1ZmZpY2llbnRseSBhbmQgc2ltaWxhcmx5IGRpdmVyc2UuICBNYW55IHNhbXBsZXMgd2VyZQpvYnNlcnZlZCBuZWFyIG9yIGF0IHRoZSBwdXRhdGl2ZSBhc3ltcHRvdGUgb2YgbGlrZWx5IGdlbmUgY29udGVudDsgbm8Kc2FtcGxlcyB3ZXJlIG9ic2VydmVkIHdpdGggZmV3ZXIgdGhhbiA2NSUgb2YgdGhlIExlaXNobWFuaWEgcGFuYW1lbnNpcwpnZW5lcyBpbmNsdWRlZC4gIE5vdGUgdGhhdCB0aGUgcmFuZ2Ugb2YgZ2VuZXMgb2JzZXJ2ZWQgaXMgcXVpdGUgc21hbGwsCjg1MDAgPD0geCA8IDg3MDAgZ2VuZXMsIGhvd2V2ZXIgdGhpcyB3YXMgcGxvdHRlZCBhZnRlciBhbHJlYWR5CmV4Y2x1ZGluZyBzYW1wbGVzIHdpdGggZmV3ZXIgdGhhbiA4NTAwIGdlbmVzIG9ic2VydmVkIChvZiB3aGljaCB0aGVyZQp3ZXJlIDIpIGFuZCBhbnkgc2FtcGxlcyB3aXRoIGZld2VyIHRoYW4gNSBtaWxsaW9uIHByb3RlaW4gY29kaW5nCm1hcHBlZCByZWFkcyAodGhlcmUgd2VyZSAyIHNhbXBsZXMgdGhhdCBoYWQgbW9yZSB0aGFuIDg1MDAgZ2VuZXMKb2JzZXJ2ZWQgaW4gbGVzcyB0aGFuIDUgbWlsbGlvbiByZWFkcykuCgpgYGB7cn0KbHBfYm94IDwtIHBsb3RfYm94cGxvdChscF9zZSkKZGV2IDwtIHBwKGZpbGUgPSAiaW1hZ2VzL2xwX3NlX2JveHBsb3QucG5nIiwgd2lkdGggPSAxNiwgaGVpZ2h0ID0gOSkKbHBfYm94CmNsb3NlZCA8LSBkZXYub2ZmKCkKbHBfYm94CmBgYAoKVGhlIGRpc3RyaWJ1dGlvbiBvZiBvYnNlcnZlZCBjb3VudHMgLyBnZW5lIGZvciBhbGwgc2FtcGxlcyB3YXMgcGxvdHRlZAphcyBhIGJveHBsb3Qgb24gdGhlIGxvZzIgKGl0IGxvb2tzIGxpa2UgaXQgaXMgbG9nMTAsIGJ1dCBJIGNoZWNrZWQpCnNjYWxlLiAgSW4gY29udHJhc3QgdG8gaG9zdCB0cmFuc2NyaXB0b21lIGRpc3RyaWJ1dGlvbiwgdGhlIHBhcmFzaXRlCmRpc3RyaWJ1dGlvbiBvZiByZWFkcy9nZW5lIGlzIGxvZy1ub3JtYWwuCgpgYGB7cn0KZmlsdGVyX3Bsb3QgPC0gcGxvdF9saWJzaXplX3ByZXBvc3QobHBfc2UpCmZpbHRlcl9wbG90JGxvd2dlbmVfcGxvdApmaWx0ZXJfcGxvdCRjb3VudF9wbG90CmBgYAoKVGhlIG51bWJlcnMgb2YgZ2VuZXMgcmVtb3ZlZCBieSBsb3ctY291bnQgZmlsdGVyaW5nIGlzIGRyYXN0aWNhbGx5Cmxvd2VyIGluIHBhcmFzaXRlIHNhbXBsZXMgdGhhbiBodW1hbi4gIFRodXMsIGV2ZW4gdGhvdWdoIHRoZSByYW5nZSBvZgpjb3ZlcmFnZSBmb3IgdGhlIHBhcmFzaXRlIHNhbXBsZXMgaXMgZnJvbSBuZWFyIDAgdG8gfiAxNTAgQ1BNLCB0aGUKbnVtYmVyIG9mIGdlbmVzIHJlbW92ZWQgYnkgdGhlIGRlZmF1bHQgbG93LWNvdW50IGZpbHRlciByYW5nZXMgb25seQpmcm9tIDQwIHRvIDEyOSwgYW5kIHRoZSBudW1iZXIgb2YgcmVhZHMgYXNzb2NpYXRlZCB3aXRoIHRoZW0gcmFuZ2VzCm9ubHkgZnJvbSAxMDAgdG8gMzE2OC4KCmBgYHtyfQp0YWJsZShjb2xEYXRhKGxwX3NlKVtbInp5bW9kZW1lY2F0ZWdvcmljYWwiXV0pCnRhYmxlKGNvbERhdGEobHBfc2UpW1siY2xpbmljYWxyZXNwb25zZSJdXSkKYGBgCgojIFRyYW5zY3JpcHRvbWUgdmlzdWFsaXphdGlvbnMKCiMjIERpc3RyaWJ1dGlvbiBWaXN1YWxpemF0aW9ucwoKTmFqaWIncyBmYXZvcml0ZSBwbG90cyBhcmUgb2YgY291cnNlIHRoZSBQQ0EvVE5TRS4gIFRoZXNlIGFyZSBuaWNlIHRvIGxvb2sgYXQgaW4Kb3JkZXIgdG8gZ2V0IGEgc2Vuc2Ugb2YgdGhlIHJlbGF0aW9uc2hpcHMgYmV0d2VlbiBzYW1wbGVzLiAgVGhleSBhbHNvIHByb3ZpZGUgYQpnb29kIG9wcG9ydHVuaXR5IHRvIHNlZSB3aGF0IGhhcHBlbnMgd2hlbiBvbmUgYXBwbGllcyBkaWZmZXJlbnQgbm9ybWFsaXphdGlvbnMsCnN1cnJvZ2F0ZSBhbmFseXNlcywgZmlsdGVycywgZXRjLiAgSW4gYWRkaXRpb24sIG9uZSBtYXkgc2V0IGRpZmZlcmVudApleHBlcmltZW50YWwgZmFjdG9ycyBhcyB0aGUgcHJpbWFyeSAnY29uZGl0aW9uJyAodXN1YWxseSB0aGUgY29sb3Igb2YgcGxvdHMpIGFuZApzdXJyb2dhdGUgJ2JhdGNoZXMnLgoKIyMgQnkgU3VzY2VwdGlsaWJpdHkKCkNvbHVtbiAnUScgaW4gdGhlIHNhbXBsZSBzaGVldCwgbWFrZSBhIGNhdGVnb3JpY2FsIHZlcnNpb24gb2YgaXQgd2l0aCB0aGVzZSBwYXJhbWV0ZXJzOgoKKiAwIDw9IHggPD0gMzUgaXMgcmVzaXN0YW50CiogMzYgPD0geCA8PSA0OCBpcyBhbWJpZ3VvdXMKKiA0OSA8PSB4IGlzIHNlbnNpdGl2ZQoKYGBge3J9CnN0cmFpbl9ub3JtIDwtIG5vcm1hbGl6ZShscF9zdHJhaW4sIG5vcm0gPSAicXVhbnQiLCB0cmFuc2Zvcm0gPSAibG9nMiIsCiAgICAgICAgICAgICAgICAgICAgICAgICBjb252ZXJ0ID0gImNwbSIsIGZpbHRlciA9IFRSVUUpCnp5bW9fcGNhIDwtIHBsb3RfcGNhKHN0cmFpbl9ub3JtLCBwbG90X3RpdGxlID0gIlBDQSBvZiBwYXJhc2l0ZSBleHByZXNzaW9uIHZhbHVlcyIsCiAgICAgICAgICAgICAgICAgICAgIHBsb3RfbGFiZWxzID0gRkFMU0UpCnp5bW9fcGNhCmRldiA8LSBwcChmaWxlID0gImZpZ3VyZXMvcHJvbWFzdGlnb3RlX3p5bW9jb2xfc2Vuc3NoYXBlX3oyMV90b196MjQucGRmIikKenltb19wY2EkcGxvdApjbG9zZWQgPC0gZGV2Lm9mZigpCgpscF9zdHJhaW5fa25vd24gPC0gc3Vic2V0X3NlKGxwX3N0cmFpbiwgc3Vic2V0ID0gImNsaW5pY2FsY2F0ZWdvcmljYWwhPSd1bmtub3duJyIpCnN0cmFpbl9rbm93bl9ub3JtIDwtIG5vcm1hbGl6ZShscF9zdHJhaW5fa25vd24sIG5vcm0gPSAicXVhbnQiLCB0cmFuc2Zvcm0gPSAibG9nMiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb252ZXJ0ID0gImNwbSIsIGZpbHRlciA9IFRSVUUpCnp5bW9fa25vd25fcGNhIDwtIHBsb3RfcGNhKHN0cmFpbl9rbm93bl9ub3JtLCBwbG90X3RpdGxlID0gIlBDQSBvZiBwYXJhc2l0ZSBleHByZXNzaW9uIHZhbHVlcyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgIHBsb3RfbGFiZWxzID0gRkFMU0UpCnp5bW9fa25vd25fcGNhCmRldiA8LSBwcChmaWxlID0gImZpZ3VyZXMvcHJvbWFzdGlnb3RlX3p5bW9jb2xfc2Vuc3NoYXBlX3oyMV90b196MjRfb25seV9rbm93bl9jbGluaWNhbC5wZGYiKQp6eW1vX2tub3duX3BjYSRwbG90CmNsb3NlZCA8LSBkZXYub2ZmKCkKYGBgCgojIyBMaW1pdCB0byB0aHJlZSBzdHJhaW5zOiAyLjEvMi4yLzIuMwoKYGBge3J9Cm9ubHlfdGhyZWVfdHlwZXMgPC0gc3Vic2V0X3NlKGxwX3N0cmFpbiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc3Vic2V0ID0gImNvbmRpdGlvbj09J3oyLjEnfGNvbmRpdGlvbj09J3oyLjMnfGNvbmRpdGlvbj09J3oyLjInIikKb25seV90aHJlZV9ub3JtIDwtIG5vcm1hbGl6ZShvbmx5X3RocmVlX3R5cGVzLCBub3JtID0gInF1YW50IiwgdHJhbnNmb3JtID0gImxvZzIiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbnZlcnQgPSAiY3BtIiwgYmF0Y2ggPSBGQUxTRSwgZmlsdGVyID0gVFJVRSkgJT4lCiAgc2V0X2JhdGNoZXMoZmFjdCA9ICJwaGFzZSIpCm9ubHl0aHJlZV9wY2EgPC0gcGxvdF9wY2Eob25seV90aHJlZV9ub3JtLCBwbG90X2xhYmVscyA9IEZBTFNFLAogICAgICAgICAgICAgICAgICAgICAgICAgIHBsb3RfdGl0bGUgPSAiUENBIG9mIHoyLjEsIHoyLjIgYW5kIHoyLjMgcGFyYXNpdGUgZXhwcmVzc2lvbiB2YWx1ZXMiKQpwcChmaWxlID0gImltYWdlcy9wcm9tYXN0aWdvdGVfdGhyZWV0eXBlc196eW1vY29sX25vc2hhcGUucG5nIikKb25seXRocmVlX3BjYSRwbG90CmRldi5vZmYoKQpvbmx5dGhyZWVfcGNhCmBgYAoKIyMgQnkgbXkgTUwga25uIGNsYXNzaWZpZXIhCgpJIGFkZGVkIHRoZSByZXN1bHQgZnJvbSBteSBrbWVyIGNsYXNzaWZpZXIgdG8gdGhlIHNhbXBsZSBzaGVldCwgbGV0IHVzCnNlZSBob3cgdGhhdCBsb29rcy4KCmBgYHtyfQpscF9zdHJhaW5fa25uIDwtIHNldF9jb25kaXRpb25zKGxwX3N0cmFpbiwgZmFjdCA9ICJrbm52MmNsYXNzaWZpY2F0aW9uIikKc3RyYWluX25vcm1fa25uIDwtIG5vcm1hbGl6ZShscF9zdHJhaW5fa25uLCBub3JtID0gInF1YW50IiwgdHJhbnNmb3JtID0gImxvZzIiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbnZlcnQgPSAiY3BtIiwgZmlsdGVyID0gVFJVRSkKenltb19wY2Ffa25uIDwtIHBsb3RfcGNhKHN0cmFpbl9ub3JtX2tubiwgcGxvdF90aXRsZSA9ICJQQ0Egb2YgcGFyYXNpdGUgZXhwcmVzc2lvbiB2YWx1ZXMiLAogICAgICAgICAgICAgICAgICAgICAgICAgcGxvdF9sYWJlbHMgPSBGQUxTRSkKZGV2IDwtIHBwKGZpbGUgPSAiaW1hZ2VzL3Byb21hc3RpZ290ZV96eW1vY29sX3NlbnNzaGFwZV9rbm52Mi5wbmciKQp6eW1vX3BjYV9rbm4kcGxvdApjbG9zZWQgPC0gZGV2Lm9mZigpCnp5bW9fcGNhX2tubgoKc3RyYWluX25vYmF0Y2ggPC0gc2V0X2JhdGNoZXMoc3RyYWluX25vcm0sIGZhY3QgPSAic291cmNlbGFiIikKenltb19wY2F2MiA8LSBwbG90X3BjYShzdHJhaW5fbm9iYXRjaCwgcGxvdF90aXRsZSA9ICJQQ0Egb2YgcGFyYXNpdGUgZXhwcmVzc2lvbiB2YWx1ZXMiLAogICAgICAgICAgICAgICAgICAgICAgIHBsb3RfbGFiZWxzID0gRkFMU0UpCmRldiA8LSBwcChmaWxlID0gImltYWdlcy9wcm9tYXN0aWdvdGVfenltb2NvbF9ub2JhdGNoLnBuZyIpCnp5bW9fcGNhdjIkcGxvdApjbG9zZWQgPC0gZGV2Lm9mZigpCnp5bW9fcGNhdjIKCnN0cmFpbl9uYiA8LSBub3JtYWxpemUobHBfc3RyYWluLCBjb252ZXJ0ID0gImNwbSIsIHRyYW5zZm9ybSA9ICJsb2cyIiwKICAgICAgICAgICAgICAgICAgICAgICBmaWx0ZXIgPSBUUlVFLCBiYXRjaCA9ICJzdmFzZXEiKQpzdHJhaW5fbmJfcGNhIDwtIHBsb3RfcGNhKHN0cmFpbl9uYiwgcGxvdF90aXRsZSA9ICJQQ0Egb2YgcGFyYXNpdGUgZXhwcmVzc2lvbiB2YWx1ZXMiLAogICAgICAgICAgICAgICAgICAgICAgICAgIHBsb3RfbGFiZWxzID0gRkFMU0UpCmRldiA8LSBwcChmaWxlID0gImltYWdlcy9jbGluaWNhbF9uYl9wY2Ffc3VzX3NoYXBlLnBuZyIpCnN0cmFpbl9uYl9wY2EkcGxvdApjbG9zZWQgPC0gZGV2Lm9mZigpCnN0cmFpbl9uYl9wY2EKYGBgCgojIyMgU2lsbHkgcGxvdGx5CgpgYGB7cn0KcGxvdGx5X3Bsb3QgPC0gcGxvdGx5OjpnZ3Bsb3RseSh6eW1vX3BjYV9rbm4kcGxvdCkKcHJpbnQocGxvdGx5X3Bsb3QpCmBgYAoKQWRkIGV4cGxpY2l0IGxhYmVscyBmb3IgYSBmZXcgcmVmZXJlbmNlIHN0cmFpbnM6CgoqIFRNUkMyMDAyMzogRXhjbHVkZWQgZHVlIHRvIGNvdmVyYWdlIChvbmx5IDdrIHJlYWRzKQoqIFRNUkMyMDAwNjogVGhpcyBvbmUgaGFzIDE5LDgxNSw2NzMgcmVhZHMsIGJ1dCBhIHdlaXJkbHkgc21hbGwgbnVtYmVyIG9mIGdlbmVzIGFuZCBnb3QgZXhjbHVkZWQuCiogVE1SQzIwMDI5OiBUaGlzIGhhcyAxLDk0Niw5ODYgcmVhZHMgYW5kIHNvIHdhcyBleGNsdWRlZC4KKiBUTVJDMjAwMzQ6IE5vdCBzZXF1ZW5jZWQKCioqIE5PVEUgKiogVGhlc2Ugc2FtcGxlcyB3ZXJlIGFsbCByZW1vdmVkIGZyb20gZXhhbWluYXRpb24gaW4gdGhlCl9zYW1wbGVfc2hlZXRfIGluIDIwMjQwNCBhbmQgc28gd2lsbCBub3QgYXBwZWFyIGluIHRoaXMgcGxvdC4gIFRodXMgSQphbSB0dXJuaW5nIG9mZiB0aGUgZm9sbG93aW5nIGJsb2NrLgoKYGBge3IsIGV2YWw9RkFMU0V9CnNhbXBsZXNfdG9fbGFiZWwgPC0gYygiVE1SQzIwMDIzIiwgIlRNUkMyMDAwNiIsICJUTVJDMjAwMjkiLCAiVE1SQzIwMDA3IiwgIlRNUkMyMDAzNCIsCiAgICAgICAgICAgICAgICAgICAgICAiVE1SQzIwMDA4IiwgIlRNUkMyMDAyNyIsICJUTVJDMjAwMjgiLCAiVE1SQzIwMDMyIiwgIlRNUkMyMDA0MCIpCgpsYWJlbF9lbnRyaWVzIDwtIHp5bW9fcGNhJHRhYmxlW3NhbXBsZXNfdG9fbGFiZWwsIF0Kenltb19wY2EkcGxvdCArCiAgZ2VvbV90ZXh0KG1hcHBpbmcgPSBhZXMoeCA9ICJQQzEiLCB5ID0gIlBDMiIsIGxhYmVsID0gInNhbXBsZWlkIiksCiAgICAgICAgICAgIGRhdGEgPSBsYWJlbF9lbnRyaWVzKQpgYGAKClNvbWUgbGlrZWx5IHRleHQgZm9yIGEgZmlndXJlIGxlZ2VuZCBtaWdodCBpbmNsdWRlIHNvbWV0aGluZyBsaWtlIHRoZQpmb2xsb3dpbmcgKHBhcmFwaHJhc2VkIGZyb20gTmFqaWIncyAyMDE2IGR1YWwgdHJhbnNjcmlwdG9tZSBwcm9maWxpbmcKcGFwZXIgKDEwLjExMjgvbUJpby4wMDAyNy0xNikpOgoKRXhwcmVzc2lvbiBwcm9maWxlcyBvZiB0aGUgcHJvbWFzdGlnb3RlIHNhbXBsZXMgYWNyb3NzIG11bHRpcGxlCnN0cmFpbnMuIEVhY2ggZ2x5cGggcmVwcmVzZW50cyBvbmUgc2FtcGxlLCBjb2xvcnMgZGVsaW5lYXRlIHRoZQp2YXJpb3VzIHN0cmFpbnMgYW5kIGZhbGwgaW50byB0d28gcHJpbWFyeSBjbGFkZXMuICBSZWQgc2FtcGxlcyBhcmUKenltb2RlbWUgMi4zLCBibHVlIHNhbXBsZXMgYXJlIHp5bW9kZW1lIDIuMi4gIFRoZSBkaWZmZXJlbmNlIGJldHdlZW4KdGhlc2UgdHdvIHByaW1hcnkgZ3JvdXBzIG1ha2UgdXAgYXBwcm94aW1hdGVseSAxNyUgb2YgdGhlIHZhcmlhbmNlIGluCnRoZSBQQ0EuICBQdXJwbGUgc2FtcGxlcyBhcmUgTGVpc2htYW5pYSBicmF6aWxpZW5zaXMgb3Igenltb2RlbWUKMS4wLzEuNSBzYW1wbGVzLCBvcmFuZ2UgYXJlIHoyLjQsIGJyb3ducyBhbmQgZ3JleXMgYXJlIHoyLjEsIHoyLjAsCnozLjAsIGFuZCB6My4yIHJlc3BlY3RpdmVseS4gIFRoaXMgYW5hbHlzaXMgd2FzIHBlcmZvcm1lZCBmb2xsb3dpbmcgYQpsb3ctY291bnQgZmlsdGVyLCBjcG0gY29udmVyc2lvbiwgcXVhbnRpbGUgbm9ybWFsaXphdGlvbiwgYW5kIGEgbG9nMgp0cmFuc2Zvcm1hdGlvbi4gIE5vIGJhdGNoIGZhY3RvciB3YXMgdXNlZCwgbm9yIHdhcyBhIHN1cnJvZ2F0ZQp2YXJpYWJsZSBlc3RpbWF0aW9uIHBlcmZvcm1lZC4KClNvbWUgaW50ZXJwcmV0YXRpb24gZm9yIHRoaXMgZmlndXJlIG1pZ2h0IGluY2x1ZGU6CgpXaGVuIFBDQSB3YXMgcGVyZm9ybWVkIG9uIHRoZSBwcm9tYXN0aWdvdGUgc2FtcGxlcywgdGhlIGRvbWluYW50IChidXQKc3RpbGwgcmVsYXRpdmVseSBzbWFsbCBhbW91bnQgb2YgdmFyaWFuY2UpIGNvbXBvbmVudCBvYnNlcnZlZApjb2luY2lkZWQgd2l0aCB0aGUgdHdvIHByaW1hcnkgc3RyYWluIGdyb3Vwcywgenltb2RlbWUgMi4yIGFuZCAyLjMuCldpdGggdGhlIGV4Y2VwdGlvbiBvZiBzb21lIExlaXNobWFuaWEgYnJhemlsaWVuc2lzIHNhbXBsZXMsIGFsbApwcm9tYXRpZ290ZSBzYW1wbGVzIGFzc2F5ZWQgZmVsbCBpbnRvIG9uZSBvZiB0aGVzZSB0d28gY2F0ZWdvcmllcy4KCldoZW4gc3Vycm9nYXRlIHZhcmlhbGJlIGVzdGltYXRpb24gd2FzIHBlcmZvcm1lZCBvbiB0aGUgZW50aXJlIHNldCBvZgpzYW1wbGVzLCBpdCBpbmNyZWFzZWQgdGhlIGFwcGFyZW50IHN0cmFpbi1kZXBlbmRlbnQgdmFyaWFuY2UsIGJ1dCBoYWQKc29tZSBwb3RlbnRpYWxseSBwcm9ibGVtYXRpYyBlZmZlY3RzIGZvciBhIGNvdXBsZSBvZiBzYW1wbGVzIChvbmUgejIuMwpzYW1wbGUgbm93IGxpZXMgd2l0aCB0aGUgb3RoZXIgejIuMiBzYW1wbGVzKTsgaXQgaXMgYXNzdW1lZCB0aGF0IHRoaXMKaXMgYmVjYXVzZSBzdmEgYXR0ZW1wdGVkIHRvIGVzdGltYXRlIHN1cnJvZ2F0ZSB2YWx1ZXMgZm9yIHRoZQpsZXNzLXJlcHJlc2VudGVkIHN0cmFpbnMgd2l0aCBzb21lIHVuaW50ZW5kZWQgY29uc2VxdWVuY2VzIGZvciBzYW1wbGUKVE1SQzIwMDk1ICh3aGljaCwgYWxvbmcgd2l0aCBUTVJDMjAwMDggYXJlIHRoZSB0d28gbGVhc3QgY292ZXJlZApzYW1wbGVzIGJ5IGEgc2lnbmlmaWNhbnQgbWFyZ2luKTsgdGhpcyBoeXBvdGhlc2lzIG1heSBiZSB0ZXN0ZWQgYnkKZXhjbHVkaW5nIHRoZSBicmF6aWxpZW5zaXMgYW5kIG5vbi16Mi4yLzIuMyBzYW1wbGVzIGFuZCByZXBlYXRpbmcKKHdoZW4gdGhpcyBpcyBwZXJmb3JtZWQgbGF0ZXIgaW4gdGhlIGRvY3VtZW50LCB0aGUgZGlmZmVyZW5jZSBiZXR3ZWVuCnRoZSB0d28gcHJpbWFyeSBjbGFkZXMgaW5jcmVhc2VzIHRvIDQ5LjMzJSBvZiB0aGUgdmFyaWFuY2UgYW5kIHRoZXJlCmFyZSBubyBvZGQgc2FtcGxlcykuCgpgYGB7cn0Kenltb190c25lIDwtIHBsb3RfdHNuZShzdHJhaW5fbm9ybSwgcGxvdF90aXRsZSA9ICJUU05FIG9mIHBhcmFzaXRlIGV4cHJlc3Npb24gdmFsdWVzIikKenltb190c25lCgpzdHJhaW5fbmJfdHNuZSA8LSBwbG90X3RzbmUoc3RyYWluX25iLCBwbG90X3RpdGxlID0gIlRTTkUgb2YgcGFyYXNpdGUgZXhwcmVzc2lvbiB2YWx1ZXMiKQpzdHJhaW5fbmJfdHNuZQoKY29yaGVhdCA8LSBwbG90X2NvcmhlYXQoc3RyYWluX25vcm0sIHBsb3RfdGl0bGUgPSAiQ29ycmVsYXRpb24gaGVhdG1hcCBvZiBwYXJhc2l0ZQogICAgICAgICAgICAgICAgIGV4cHJlc3Npb24gdmFsdWVzCiIpCmNvcmhlYXQKCmRpc2hlYXQgPC0gcGxvdF9kaXNoZWF0KHN0cmFpbl9ub3JtLCBwbG90X3RpdGxlID0gIkRpc3RhbmNlIGhlYXRtYXAgb2YgcGFyYXNpdGUKICAgICAgICAgICAgICAgICBleHByZXNzaW9uIHZhbHVlcwoiKQpkaXNoZWF0JHBsb3QKCnBsb3Rfc20oc3RyYWluX25vcm0pCmBgYAoKUG90ZW50aWFsIHN0YXJ0IGZvciBhIGZpZ3VyZSBsZWdlbmQ6CgpHbG9iYWwgcmVsYXRpb25zaGlwcyBhbW9uZyB0aGUgcHJvbWFzdGlnb3RlIHRyYW5zY3JpcHRpb25hbApwcm9maWxlcy4gIFBhaXJ3aXNlIHBlYXJzb24gY29ycmVsYXRpb25zIGFuZCBFdWNsaWRlYW4gZGlzdGFuY2VzIHdlcmUKY2FsY3VsYXRlZCB1c2luZyB0aGUgbm9ybWFsaXplZCBleHByZXNzaW9uIG1hdHJpY2VzLiAgQ29sb3JzIGFsb25nIHRoZQp0b3Agcm93IGRlbGluZWF0ZSB0aGUgZXhwZXJpbWVudGFsIGNvbmRpdGlvbnMgKHNhbWUgY29sb3JzIGFzIHRoZSBQQ0EpClNhbXBsZXMgd2VyZSBjbHVzdGVyZWQgYnkgbmVhcmVzdCBuZWlnaGJvciBjbHVzdGVyaW5nIGFuZCBlYWNoIGNvbG9yZWQKdGlsZSBkZXNjcmliZXMgb25lIGNvcnJlbGF0aW9uIHZhbHVlIGJldHdlZW4gdHdvIHNhbXBsZXMgKHJlZCB0byB3aGl0ZQpkZWxpbmVhdGVzIHBlYXJzb24gY29ycmVsYXRpb24gdmFsdWVzIG9mIHRoZSA4LDcxMCBub3JtYWxpemVkIGdlbmUKdmFsdWVzIGJldHdlZW4gdHdvIHNhbXBsZXMgcmFuZ2luZyBmcm9tIDw9IDAuNyB0byA+PSAxLjApIG9yCnRoZSBldWNsaWRlYW4gZGlzdGFuY2UgYmV0d2VlbiB0d28gc2FtcGxlcyAoZGFyayBibHVlIHRvIHdoaXRlCmRlbGluZWF0ZXMgaWRlbnRpY2FsIHRvIGEgbm9ybWFsaXplZCBldWNsaWRlYW4gZGlzdGFuY2Ugb2YgPj0gMTEwKS4KClNvbWUgaW50ZXJwcmV0YXRpb24gZm9yIHRoaXMgZmlndXJlIG1pZ2h0IGluY2x1ZGU6CgpXaGVuIHRoZSBnbG9iYWwgcmVsYXRpb25zaGlwcyBhbW9uZyB0aGUgc2FtcGxlcyB3ZXJlIGRpc3RpbGxlZCBkb3duIHRvCmluZGl2aWR1YWwgZXVjbGlkZWFuIGRpc3RhbmNlcyBvciBwZWFyc29uIGNvcnJlbGF0aW9uIGNvZWZmaWNpZW50cwpiZXR3ZWVuIHBhaXJzIG9mIHNhbXBsZXMsIHRoZSBwcmltYXJ5IGNsdXN0ZXJpbmcgYW1vbmcgc2FtcGxlcwpvYnNlcnZlZCB3YXMgYWNjb3JkaW5nIHRvIHN0cmFpbi4gIFRoZSBwcmltYXJ5IHNpZ25pZmljYW50IG91dGxpZXIKc2FtcGxlIChUTVJDMjAwOTUpIGlzIGV4cGxpY2l0bHkgZHVlIHRvIGxvdyBjb3ZlcmFnZS4gIFRoZSBvdGhlcgpvdXRsaWVyIHN0cmFpbnMgYXJlIGVpdGhlciBicmF6aWxpZW5zaXMgKHB1cnBsZSkgb3IgYSBzZXJpZXMgb2YKc3RyYWlucyB3aGljaCwgd2hlbiB2aWV3ZWQgaW4gSUdWLCBhcHBlYXIgdG8gaGF2ZSBnZW5ldGljIHZhcmlhbnRzCndoaWNoIGJyaWRnZSB0aGUgZGlmZmVyZW5jZXMgYmV0d2VlbiB0aGUgdHdvIHByaW1hcnkgenltb2RlbWVzLApwYXJ0aWN1bGFybHkgb24gdGhlIGtub3duIGFuZXVwbG9pZCBjaHJvbW9zb21lcy4KCiMjIExpbWl0IHRvIGp1c3QgdHdvIHN0cmFpbnM6IDIuMi8yLjMKCmBgYHtyfQpscF90d29fc3RyYWluc19ub3JtIDwtIG5vcm1hbGl6ZShscF96eW1vLCBub3JtID0gInF1YW50IiwgdHJhbnNmb3JtID0gImxvZzIiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb252ZXJ0ID0gImNwbSIsIGJhdGNoID0gRkFMU0UsIGZpbHRlciA9IFRSVUUpCm9ubHl0d29fcGNhIDwtIHBsb3RfcGNhKGxwX3R3b19zdHJhaW5zX25vcm0sIHBsb3RfdGl0bGUgPSAiUENBIG9mIHoyLjIgYW5kIHoyLjMgcGFyYXNpdGUgZXhwcmVzc2lvbiB2YWx1ZXMiLAogICAgICAgICAgICAgICAgICAgICAgICBwbG90X2xhYmVscyA9IEZBTFNFKQpkZXYgPC0gcHAoZmlsZSA9ICJmaWd1cmVzL3p5bW9fejIuMl96Mi4zX3BjYV9zdXNfc2hhcGUucGRmIikKb25seXR3b19wY2EkcGxvdApjbG9zZWQgPC0gZGV2Lm9mZigpCm9ubHl0d29fcGNhJHBsb3QKCmxwX3R3b19zdHJhaW5zX2tub3duIDwtIHN1YnNldF9zZShscF96eW1vLCBzdWJzZXQgPSAiY2xpbmljYWxjYXRlZ29yaWNhbCE9J3Vua25vd24nIikKbHBfdHdvX3N0cmFpbnNfa25vd25fbm9ybSA8LSBub3JtYWxpemUobHBfdHdvX3N0cmFpbnNfa25vd24sIG5vcm0gPSAicXVhbnQiLCB0cmFuc2Zvcm0gPSAibG9nMiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbnZlcnQgPSAiY3BtIiwgYmF0Y2ggPSBGQUxTRSwgZmlsdGVyID0gVFJVRSkKb25seXR3b19rbm93bl9wY2EgPC0gcGxvdF9wY2EobHBfdHdvX3N0cmFpbnNfa25vd25fbm9ybSwgcGxvdF9sYWJlbHMgPSBGQUxTRSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcGxvdF90aXRsZSA9ICJQQ0Egb2YgejIuMiBhbmQgejIuMyBwYXJhc2l0ZSBleHByZXNzaW9uIHZhbHVlcyIpCmRldiA8LSBwcChmaWxlID0gImZpZ3VyZXMvenltb196Mi4yX3oyLjNfcGNhX3N1c19zaGFwZV9vbmx5X2tub3duLnBkZiIpCm9ubHl0d29fcGNhJHBsb3QKY2xvc2VkIDwtIGRldi5vZmYoKQpvbmx5dHdvX3BjYQoKbHBfdHdvX3N0cmFpbnNfbmIgPC0gbm9ybWFsaXplKGxwX3p5bW8sIHRyYW5zZm9ybSA9ICJsb2cyIiwgY29udmVydCA9ICJjcG0iLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYmF0Y2ggPSAic3Zhc2VxIiwgZmlsdGVyID0gVFJVRSkKb25seXR3b19wY2FfbmIgPC0gcGxvdF9wY2EobHBfdHdvX3N0cmFpbnNfbmIsIHBsb3RfbGFiZWxzID0gRkFMU0UsCiAgICAgICAgICAgICAgICAgICAgICAgICAgIHBsb3RfdGl0bGUgPSAiUENBIG9mIHoyLjIgYW5kIHoyLjMgcGFyYXNpdGUgZXhwcmVzc2lvbiB2YWx1ZXMiKQpkZXYgPC0gcHAoZmlsZSA9ICJpbWFnZXMvenltb196Mi4yX3oyLjNfcGNhX3N1c19zaGFwZV9uYi5wZGYiKQpvbmx5dHdvX3BjYV9uYiRwbG90CmNsb3NlZCA8LSBkZXYub2ZmKCkKb25seXR3b19wY2FfbmIkcGxvdApgYGAKCiMjIEJ5IEN1cmUvRmFpbCBzdGF0dXMKClRoaXMgaXMgYnkgZmFyIHRoZSBtb3N0IHByb2JsZW1hdGljIGNvbXBhcmlzb24sIEkgdGhpbmsgdGhlIG9ubHkKaW50ZXJwcmV0YXRpb24gb2YgdGhlIGZvbGxvd2luZyBpbWFnZXMgaXMgdGhhdCB0aGUgcGFyYXNpdGUgaGFzIGxpdHRsZQplZmZlY3Qgb24gdGhlIGxpa2VsaWhvb2QgdGhhdCBhIHBlcnNvbiB3aWxsIHN1Y2Nlc3NmdWxseSBlbmQKdHJlYXRtZW50LiAgVGhlcmUgZG9lcyBhcHBlYXIgdG8gYmUgc29tZSB2YXJpYW5jZSBhc3NvY2lhdGVkIHdpdGgKY3VyZS9mYWlsLCBidXQgb25seSBpbiBhIGZldyBzYW1wbGVzICh2aXNpYmxlIGluIH4xMCBmYWlsIHNhbXBsZXMgYW5kCnBlcmhhcHMgfjggY3VyZSBzYW1wbGVzIHdoZW4gc3ZhIGlzIGFwcGxpZWQgdG8gdGhlIGRhdGEpLgoKYGBge3J9CmNmX25vcm0gPC0gbm9ybWFsaXplKGxwX2NmLCBjb252ZXJ0ID0gImNwbSIsIHRyYW5zZm9ybSA9ICJsb2cyIiwKICAgICAgICAgICAgICAgICAgICAgbm9ybSA9ICJxdWFudCIsIGZpbHRlciA9IFRSVUUpCnN0YXJ0X2NmIDwtIHBsb3RfcGNhKGNmX25vcm0sIHBsb3RfdGl0bGUgPSAiUENBIG9mIHBhcmFzaXRlIGV4cHJlc3Npb24gdmFsdWVzIiwKICAgICAgICAgICAgICAgICAgICAgcGxvdF9sYWJlbHMgPSBGQUxTRSkKZGV2IDwtIHBwKGZpbGUgPSAiZmlndXJlcy9jdXJlX2ZhaWxfc3VzX3NoYXBlX2FsbC5wZGYiKQpzdGFydF9jZiRwbG90CmNsb3NlZCA8LSBkZXYub2ZmKCkKc3RhcnRfY2YKCmxwX2NmX2tub3duIDwtIHN1YnNldF9zZShscF9jZiwgc3Vic2V0ID0gImNsaW5pY2FsY2F0ZWdvcmljYWwhPSd1bmtub3duJyIpCmNmX2tub3duX25vcm0gPC0gbm9ybWFsaXplKGxwX2NmX2tub3duLCBjb252ZXJ0ID0gImNwbSIsIHRyYW5zZm9ybSA9ICJsb2cyIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgbm9ybSA9ICJxdWFudCIsIGZpbHRlciA9IFRSVUUpCnN0YXJ0X2NmX2tub3duIDwtIHBsb3RfcGNhKGNmX2tub3duX25vcm0sIHBsb3RfdGl0bGUgPSAiUENBIG9mIHBhcmFzaXRlIGV4cHJlc3Npb24gdmFsdWVzIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgcGxvdF9sYWJlbHMgPSBGQUxTRSkKZGV2IDwtIHBwKGZpbGUgPSAiZmlndXJlcy9jdXJlX2ZhaWxfc3VzX3NoYXBlX2tub3duLnBkZiIpCnN0YXJ0X2NmX2tub3duJHBsb3QKY2xvc2VkIDwtIGRldi5vZmYoKQpzdGFydF9jZl9rbm93bgoKb25seV90d29fY2YgPC0gc2V0X2NvbmRpdGlvbnMobHBfenltbywgZmFjdCA9ICJjbGluaWNhbGNhdGVnb3JpY2FsIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29sb3JzID0gY29sb3JfY2hvaWNlc1tbImNmIl1dKSAlPiUKICBzZXRfYmF0Y2hlcyhmYWN0ID0gInN1c19jYXRlZ29yeV9jdXJyZW50IikKCm9ubHlfdHdvX2NmX25vcm0gPC0gbm9ybWFsaXplKG9ubHlfdHdvX2NmLCBub3JtID0gInF1YW50IiwgdHJhbnNmb3JtID0gImxvZzIiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb252ZXJ0ID0gImNwbSIsIGJhdGNoID0gRkFMU0UsIGZpbHRlciA9IFRSVUUpCm9ubHlfdHdvX2NmX3BjYSA8LSBwbG90X3BjYShvbmx5X3R3b19jZl9ub3JtLCBwbG90X2xhYmVscyA9IEZBTFNFLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgcGxvdF90aXRsZSA9ICJQQ0Egb2YgejIuMiBhbmQgejIuMyBwYXJhc2l0ZSBleHByZXNzaW9uIHZhbHVlcyIpCmRldiA8LSBwcChmaWxlID0gImZpZ3VyZXMvY3VyZV9mYWlsX3N1c19zaGFwZV9vbmx5ejIyX3oyMy5wZGYiKQpvbmx5X3R3b19jZl9wY2EkcGxvdApkZXYub2ZmKCkKb25seV90d29fY2ZfcGNhCgpvbmx5X3R3b19jZl9rbm93biA8LSBzdWJzZXRfc2Uob25seV90d29fY2YsIHN1YnNldCA9ICJjb25kaXRpb24hPSd1bmtub3duJyIpCm9ubHlfdHdvX2NmX2tub3duX25vcm0gPC0gbm9ybWFsaXplKG9ubHlfdHdvX2NmX2tub3duLCBub3JtID0gInF1YW50IiwgdHJhbnNmb3JtID0gImxvZzIiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb252ZXJ0ID0gImNwbSIsIGJhdGNoID0gRkFMU0UsIGZpbHRlciA9IFRSVUUpCm9ubHlfdHdvX2NmX2tub3duX3BjYSA8LSBwbG90X3BjYShvbmx5X3R3b19jZl9rbm93bl9ub3JtLCBwbG90X2xhYmVscyA9IEZBTFNFLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcGxvdF90aXRsZSA9ICJQQ0Egb2YgejIuMiBhbmQgejIuMyBwYXJhc2l0ZSBleHByZXNzaW9uIHZhbHVlcyIpCmRldiA8LSBwcChmaWxlID0gImZpZ3VyZXMvY3VyZV9mYWlsX3N1c19zaGFwZV9vbmx5ejIyX3oyM19rbm93bi5wZGYiKQpvbmx5X3R3b19jZl9rbm93bl9wY2EkcGxvdApkZXYub2ZmKCkKb25seV90d29fY2Zfa25vd25fcGNhCgpjZl9uYiA8LSBub3JtYWxpemUobHBfY2YsIGNvbnZlcnQgPSAiY3BtIiwgdHJhbnNmb3JtID0gImxvZzIiLAogICAgICAgICAgICAgICAgICAgZmlsdGVyID0gVFJVRSwgYmF0Y2ggPSAic3Zhc2VxIikKY2ZfbmJfcGNhIDwtIHBsb3RfcGNhKGNmX25iLCBwbG90X3RpdGxlID0gIlBDQSBvZiBwYXJhc2l0ZSBleHByZXNzaW9uIHZhbHVlcyIsCiAgICAgICAgICAgICAgICAgICAgICBwbG90X2xhYmVscyA9IEZBTFNFKQpkZXYgPC0gcHAoZmlsZSA9ICJpbWFnZXMvY2Zfc3VzX3NoYXJlX25iLnBuZyIpCmNmX25iX3BjYSRwbG90CmNsb3NlZCA8LSBkZXYub2ZmKCkKY2ZfbmJfcGNhCgpjZl9ub3JtIDwtIG5vcm1hbGl6ZShscF9jZiwgdHJhbnNmb3JtID0gImxvZzIiLCBjb252ZXJ0ID0gImNwbSIsCiAgICAgICAgICAgICAgICAgICAgIGZpbHRlciA9IFRSVUUsIG5vcm0gPSAicXVhbnQiKQojIyBHZXR0aW5nIGFuIGVycm9yIHdoaWNoIHJlYWxseSBkb2VzIG5vdCBtYWtlIHNlbnNlLCBJIHJhbiBpdCBtYW51YWxseSBhbmQgaXQgd29ya2VkIGZpbmUuCnRlc3QgPC0gcGNhX2luZm9ybWF0aW9uKGNmX25vcm0sIG51bV9jb21wb25lbnRzID0gNiwgcGxvdF9wY2FzID0gVFJVRSwKICAgICAgICAgICAgICAgICAgICAgICAgZmFjdG9ycyA9IGMoImNsaW5pY2FsY2F0ZWdvcmljYWwiLCAienltb2RlbWVjYXRlZ29yaWNhbCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJwYXRob2dlbnN0cmFpbiIsICJwYXNzYWdlbnVtYmVyIikpCnRlc3QkYW5vdmFfcAp0ZXN0JGNvcl9oZWF0bWFwCmBgYAoKIyMgQnkgQ3VycmVudCBkcnVnIHNlbnNpdGl2aXR5IGFzc2F5IGRhdGEKCldlIGhhdmUgdHdvIGNvbXBldGluZyBtZXRyaWNzIG9mIGFudG1vbmlhbCBzZW5zaXRpdml0eTsgb25lIGhpc3RvcmljYWwKYW5kIG9uZSBjdXJyZW50LiAgSW4gYm90aCBjYXNlcyB0aGVyZSBpcyBhIHJlYXNvbmFibGUgZXhwZWN0YXRpb24gdGhhdApyZXNpc3RhbnQgc3RyYWlucyB0ZW5kIHRvIGJlIHp5bW9kZW1lIDIuMyBhbmQgc2Vuc2l0aXZlIHN0cmFpbnMgdGVuZAp0byBiZSB6eW1vZGVtZSAyLjIuICBUaGVyZSBhcHBlYXIgdG8gYmUgbW9yZSBleGNlcHRpb25zIHRvIHRoaXMgcnVsZQpvZiB0aHVtYiBpbiB0aGUgY3VycmVudCBkYXRhIHRoYW4gdGhlIGhpc3RvcmljYWwuCgpgYGB7cn0KZGltKGFzc2F5KGxwX3N1c2NlcHRpYmlsaXR5KSkKc3VzX25vcm0gPC0gbm9ybWFsaXplKGxwX3N1c2NlcHRpYmlsaXR5LCB0cmFuc2Zvcm0gPSAibG9nMiIsIGNvbnZlcnQgPSAiY3BtIiwKICAgICAgICAgICAgICAgICAgICAgIG5vcm0gPSAicXVhbnQiLCBmaWx0ZXIgPSBUUlVFKQpzdXNfcGNhIDwtIHBsb3RfcGNhKHN1c19ub3JtLCBwbG90X3RpdGxlID0gIlBDQSBvZiBwYXJhc2l0ZSBleHByZXNzaW9uIHZhbHVlcyIsCiAgICAgICAgICAgICAgICAgICAgcGxvdF9sYWJlbHMgPSBGQUxTRSkKZGV2IDwtIHBwKGZpbGUgPSAiZmlndXJlcy9zdXNfbm9ybV9wY2Euc3ZnIikKc3VzX3BjYVtbInBsb3QiXV0KY2xvc2VkIDwtIGRldi5vZmYoKQpkZXYgPC0gcHAoZmlsZSA9ICJmaWd1cmVzL3N1c19ub3JtX3BjYS5wZGYiKQpzdXNfcGNhW1sicGxvdCJdXQpjbG9zZWQgPC0gZGV2Lm9mZigpCnN1c19wY2EKCmxwX3N1c2NlcHRpYmlsaXR5X2tub3duIDwtIHN1YnNldF9zZShscF9zdXNjZXB0aWJpbGl0eSwgc3Vic2V0ID0gImJhdGNoIT0ndW5rbm93biciKQpzdXNfa25vd25fbm9ybSA8LSBub3JtYWxpemUobHBfc3VzY2VwdGliaWxpdHlfa25vd24sIHRyYW5zZm9ybSA9ICJsb2cyIiwgY29udmVydCA9ICJjcG0iLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgbm9ybSA9ICJxdWFudCIsIGZpbHRlciA9IFRSVUUpCnN1c19rbm93bl9wY2EgPC0gcGxvdF9wY2Eoc3VzX2tub3duX25vcm0sIHBsb3RfdGl0bGUgPSAiUENBIG9mIHBhcmFzaXRlIGV4cHJlc3Npb24gdmFsdWVzIiwKICAgICAgICAgICAgICAgICAgICAgICAgICBwbG90X2xhYmVscyA9IEZBTFNFKQpkZXYgPC0gcHAoZmlsZSA9ICJmaWd1cmVzL3N1c19ub3JtX2tub3duX3BjYS5wZGYiKQpzdXNfa25vd25fcGNhW1sicGxvdCJdXQpjbG9zZWQgPC0gZGV2Lm9mZigpCnN1c19rbm93bl9wY2EKCmxwX3N1c190d28gPC0gc3Vic2V0X3NlKGxwX3N1c2NlcHRpYmlsaXR5LCBzdWJzZXQgPSAienltb2RlbWVjYXRlZ29yaWNhbCE9J3oyMSciKSAlPiUKICBzdWJzZXRfc2Uoc3Vic2V0ID0gInp5bW9kZW1lY2F0ZWdvcmljYWwhPSd6MjQnIikKc3VzX3R3b19ub3JtIDwtIG5vcm1hbGl6ZShscF9zdXNfdHdvLCB0cmFuc2Zvcm0gPSAibG9nMiIsIGNvbnZlcnQgPSAiY3BtIiwKICAgICAgICAgICAgICAgICAgICAgICAgICBub3JtID0gInF1YW50IiwgZmlsdGVyID0gVFJVRSkKc3VzX3R3b19wY2EgPC0gcGxvdF9wY2Eoc3VzX3R3b19ub3JtLCBwbG90X3RpdGxlID0gIlBDQSBvZiBwYXJhc2l0ZSBleHByZXNzaW9uIHZhbHVlcyIsCiAgICAgICAgICAgICAgICAgICAgICAgIHBsb3RfbGFiZWxzID0gRkFMU0UpCmRldiA8LSBwcChmaWxlID0gImZpZ3VyZXMvc3VzX25vcm1fdHdvX3BjYS5wZGYiKQpzdXNfdHdvX3BjYVtbInBsb3QiXV0KY2xvc2VkIDwtIGRldi5vZmYoKQpzdXNfdHdvX3BjYQoKbHBfc3VzX3R3b19rbm93biA8LSBzdWJzZXRfc2UobHBfc3VzX3R3bywgc3Vic2V0ID0gImNsaW5pY2FsY2F0ZWdvcmljYWwhPSd1bmtub3duJyIpCnN1c190d29fa25vd25fbm9ybSA8LSBub3JtYWxpemUobHBfc3VzX3R3b19rbm93biwgdHJhbnNmb3JtID0gImxvZzIiLCBjb252ZXJ0ID0gImNwbSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbm9ybSA9ICJxdWFudCIsIGZpbHRlciA9IFRSVUUpCnN1c190d29fa25vd25fcGNhIDwtIHBsb3RfcGNhKHN1c190d29fa25vd25fbm9ybSwgcGxvdF90aXRsZSA9ICJQQ0Egb2YgcGFyYXNpdGUgZXhwcmVzc2lvbiB2YWx1ZXMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwbG90X2xhYmVscyA9IEZBTFNFKQpkZXYgPC0gcHAoZmlsZSA9ICJmaWd1cmVzL3N1c19ub3JtX3R3b19rbm93bl9wY2EucGRmIikKc3VzX3R3b19rbm93bl9wY2FbWyJwbG90Il1dCmNsb3NlZCA8LSBkZXYub2ZmKCkKc3VzX3R3b19rbm93bl9wY2EKCnN1c19uYiA8LSBub3JtYWxpemUobHBfc3VzY2VwdGliaWxpdHksIHRyYW5zZm9ybSA9ICJsb2cyIiwgY29udmVydCA9ICJjcG0iLAogICAgICAgICAgICAgICAgICAgIGJhdGNoID0gInN2YXNlcSIsIGZpbHRlciA9IFRSVUUpCnN1c19uYl9wY2EgPC0gcGxvdF9wY2Eoc3VzX25iLCBwbG90X3RpdGxlID0gIlBDQSBvZiBwYXJhc2l0ZSBleHByZXNzaW9uIHZhbHVlcyIsCiAgICAgICAgICAgICAgICAgICAgICAgcGxvdF9sYWJlbHMgPSBGQUxTRSkKZGV2IDwtIHBwKGZpbGUgPSAiaW1hZ2VzL3N1c19uYl9wY2EucG5nIikKc3VzX25iX3BjYVtbInBsb3QiXV0KY2xvc2VkIDwtIGRldi5vZmYoKQpzdXNfbmJfcGNhCmBgYAoKIyMgQnkgSGlzdG9yaWNhbCBkcnVnIHNlbnNpdGl2aXR5IGFzc2F5IGRhdGEKCmBgYHtyfQpzdXNfaGlzdF9ub3JtIDwtIG5vcm1hbGl6ZShscF9zdXNjZXB0aWJpbGl0eV9oaXN0b3JpY2FsLCB0cmFuc2Zvcm0gPSAibG9nMiIsIGNvbnZlcnQgPSAiY3BtIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgbm9ybSA9ICJxdWFudCIsIGZpbHRlciA9IFRSVUUpCnN1c19oaXN0X3BjYSA8LSBwbG90X3BjYShzdXNfaGlzdF9ub3JtLCBwbG90X3RpdGxlID0gIlBDQSBvZiBwYXJhc2l0ZSBleHByZXNzaW9uIHZhbHVlcyIsCiAgICAgICAgICAgICAgICAgICAgICAgICBwbG90X2xhYmVscyA9IEZBTFNFKQpkZXYgPC0gcHAoZmlsZSA9ICJpbWFnZXMvc3VzX2hpc3Rfbm9ybV9wY2EucG5nIikKc3VzX2hpc3RfcGNhW1sicGxvdCJdXQpjbG9zZWQgPC0gZGV2Lm9mZigpCnN1c19oaXN0X3BjYQoKc3VzX2hpc3RfbmIgPC0gbm9ybWFsaXplKGxwX3N1c2NlcHRpYmlsaXR5X2hpc3RvcmljYWwsIHRyYW5zZm9ybSA9ICJsb2cyIiwgY29udmVydCA9ICJjcG0iLAogICAgICAgICAgICAgICAgICAgICAgICAgYmF0Y2ggPSAic3Zhc2VxIiwgZmlsdGVyID0gVFJVRSkKc3VzX2hpc3RfbmJfcGNhIDwtIHBsb3RfcGNhKHN1c19oaXN0X25iLCBwbG90X3RpdGxlID0gIlBDQSBvZiBwYXJhc2l0ZSBleHByZXNzaW9uIHZhbHVlcyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBwbG90X2xhYmVscyA9IEZBTFNFKQpkZXYgPC0gcHAoZmlsZSA9ICJpbWFnZXMvc3VzX2hpc3RfbmJfcGNhLnBuZyIpCnN1c19oaXN0X25iX3BjYVtbInBsb3QiXV0KY2xvc2VkIDwtIGRldi5vZmYoKQpzdXNfaGlzdF9uYl9wY2EKYGBgCgojIyBaeW1vZGVtZSBlbnp5bWUgZ2VuZSBJRHMKCk5hamliIHJlYWQgbWUgYW4gZW1haWwgbGlzdGluZyBvZmYgdGhlIGdlbmUgbmFtZXMgYXNzb2NpYXRlZCB3aXRoIHRoZSB6eW1vZGVtZQpjbGFzc2lmaWNhdGlvbi4gIEkgdG9vayB0aG9zZSBuYW1lcyBhbmQgY3Jvc3MgcmVmZXJlbmNlZCB0aGVtIGFnYWluc3QgdGhlCkxlaXNobWFuaWEgcGFuYW1lbnNpcyBnZW5lIGFubm90YXRpb25zIGFuZCBmb3VuZCB0aGUgZm9sbG93aW5nOgoKVGhleSBhcmU6CgoxLiBBTEFUOiBMUEFMMTNfMTIwMDEwOTAwIC0tIGFsYW5pbmUgYW1pbm90cmFuc2ZlcmFzZQoyLiBBU0FUOiBMUEFMMTNfMzQwMDEzMDAwIC0tIGFzcGFydGF0ZSBhbWlub3RyYW5zZmVyYXNlCjMuIEc2UEQ6IExQQUwxM18wMDAwNTQxMDAgLS0gZ2x1Y2FzZS02LXBob3NwaGF0ZSAxLWRlaHlkcm9nZW5hc2UKNC4gTkg6IExQQUwxM18xNDAwNjEwMCwgTFBBTDEzXzE4MDAxODUwMCAtLSBpbm9zaW5lLWd1YW5pbmUgbnVjbGVvc2lkZSBoeWRyb2xhc2UKNS4gTVBJOiBMUEFMMTNfMzIwMDIyMzAwIChtYXliZSkgLS0gbWFubm9zZSBwaG9zcGhhdGUgaXNvbWVyYXNlIChJIGNob3NlIHBob3NwaG9tYW5ub3NlIGlzb21lcmFzZSkKCkdpdmVuIHRoZXNlIDYgZ2VuZSBJRHMgKE5IIGhhcyB0d28gZ2VuZSBJRHMgYXNzb2NpYXRlZCB3aXRoIGl0KSwgSSBjYW4gZG8gc29tZQpsb29raW5nIGZvciBzcGVjaWZpYyBkaWZmZXJlbmNlcyBhbW9uZyB0aGUgdmFyaW91cyBzYW1wbGVzLgoKIyMjIEV4cHJlc3Npb24gbGV2ZWxzIG9mIHp5bW9kZW1lIGdlbmVzCgpUaGUgZm9sbG93aW5nIGNyZWF0ZXMgYSBjb2xvcnNwYWNlIChyZWQgdG8gZ3JlZW4pIGhlYXRtYXAgc2hvd2luZyB0aGUgb2JzZXJ2ZWQKZXhwcmVzc2lvbiBvZiB0aGVzZSBnZW5lcyBpbiBldmVyeSBzYW1wbGUuCgpgYGB7cn0KbXlfZ2VuZXMgPC0gYygiTFBBTDEzXzEyMDAxMDkwMCIsICJMUEFMMTNfMzQwMDEzMDAwIiwgIkxQQUwxM18wMDAwNTQxMDAiLAogICAgICAgICAgICAgICJMUEFMMTNfMTQwMDA2MTAwIiwgIkxQQUwxM18xODAwMTg1MDAiLCAiTFBBTDEzXzMyMDAyMjMwMCIsCiAgICAgICAgICAgICAgIm90aGVyIikKbXlfbmFtZXMgPC0gYygiQUxBVCIsICJBU0FUIiwgIkc2UEQiLCAiTkh2MSIsICJOSHYyIiwgIk1QSSIsICJvdGhlciIpCgp6eW1vX3NlIDwtIGV4Y2x1ZGVfZ2VuZXMoc3RyYWluX25vcm0sIGlkcyA9IG15X2dlbmVzLCBtZXRob2QgPSAia2VlcCIpCnp5bW9faGVhdG1hcCA8LSBwbG90X3NhbXBsZV9oZWF0bWFwKHp5bW9fc2UsIHJvd19sYWJlbCA9IG15X25hbWVzKQp6eW1vX2hlYXRtYXAKYGBgCgpBIHJlY2VudCBzdWdnZXN0aW9uIGluY2x1ZGVkIGEgcXVlcnkgYWJvdXQgdGhlIHJlbGF0aW9uc2hpcCBvZiBvdXIKYW1hc3RpZ290ZSBUTVJDMiBzYW1wbGVzIHdoaWNoIHdlcmUgdGhlIHJlc3VsdCBvZiBpbmZlY3RpbmcgYSBzZXQgb2YKbWFjcm9waGFnZXMgdnMuIHRoZXNlIHByb21hc3RpZ290ZSBzYW1wbGVzLgoKU28gZmFyLCB3ZSBoYXZlIGtlcHQgdGhlc2UgdHdvIGV4cGVyaW1lbnRzIHNlcGFyYXRlLCBub3cgbGV0IHVzIG1lcmdlIHRoZW0uCgpgYGB7cn0KdG1yYzJfbWFjcm9waGFnZV9ub3JtIDwtIG5vcm1hbGl6ZShscF9tYWNyb3BoYWdlLCB0cmFuc2Zvcm0gPSAibG9nMiIsIGNvbnZlcnQgPSAiY3BtIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBub3JtID0gInF1YW50IiwgZmlsdGVyID0gVFJVRSkKCiMjIEhleSB5b3UsIHRoaXMgYW5ub3RhdGlvbiBjYWxsIHNob3VsZCBiZSBtYWRlIGF1dG9tYXRpYyBmb3IgdGhlIGNvbnRhaW5lciEKYW5ub3RhdGlvbihscF9zZSkgPC0gIm9yZy5McGFuYW1lbnNpcy5NSE9NQ09MODFMMTMudjQ2LmVnLmRiIgphbm5vdGF0aW9uKGxwX21hY3JvcGhhZ2UpIDwtIGFubm90YXRpb24obHBfc2UpCmFsbF90bXJjMiA8LSBocGdsdG9vbHM6Ojpjb21iaW5lX3NlKGxwX3NlLCBscF9tYWNyb3BoYWdlKQpgYGAKCkJlZm9yZSB3ZSBjYW4gdXNlIHRoZSBjb21iaW5lZCBkYXRhLCB3ZSBtdXN0IHJlY29uY2lsZSBhIGZldyBvZgphc3BlY3RzIG9mIGl0LCBub3RhYmx5IHdlIG5lZWQgdG8gc3BlY2lmeSB3aGljaCBzYW1wbGVzIGFyZQphbWFzdGlnb3RlcyBhbmQgd2hpY2ggYXJlIHByb21hc3RpZ290ZXMuCgpgYGB7cn0KYWxsX25vc2IgPC0gYWxsX3RtcmMyCmNvbERhdGEoYWxsX25vc2IpW1sic3RhZ2UiXV0gPC0gInByb21hc3RpZ290ZSIKbmFfaWR4IDwtIGlzLm5hKGNvbERhdGEoYWxsX25vc2IpW1sibWFjcm9waGFnZXRyZWF0bWVudCJdXSkKY29sRGF0YShhbGxfbm9zYilbbmFfaWR4LCAibWFjcm9waGFnZXRyZWF0bWVudCJdIDwtICJ1bmRlZmluZWQiCmFsbF9ub3NiIDwtIHN1YnNldF9zZShhbGxfbm9zYiwgc3Vic2V0ID0gIm1hY3JvcGhhZ2V0cmVhdG1lbnQhPSdpbmZfc2InIikKYW1hX2lkeCA8LSBjb2xEYXRhKGFsbF9ub3NiKVtbIm1hY3JvcGhhZ2V0cmVhdG1lbnQiXV0gPT0gImluZiIKY29sRGF0YShhbGxfbm9zYilbYW1hX2lkeCwgInN0YWdlIiBdIDwtICJhbWFzdGlnb3RlIgoKIyMgTWFrZSBzdXJlIHRoYXQgdGhlIHp5bW9kZW1lIGRvZXMgbm90IGhhdmUgdGhlIGluZl8gcHJlZml4Lgp6eW1vZGVtZV9jaGFyIDwtIGdzdWIoeCA9IGNvbERhdGEoYWxsX25vc2IpW1siY29uZGl0aW9uIl1dLCBwYXR0ZXJuID0gIl5pbmZfIiwgcmVwbGFjZW1lbnQgPSAiIikKY29sRGF0YShhbGxfbm9zYilbWyJjb25kaXRpb24iXV0gPC0genltb2RlbWVfY2hhcgoKY29sRGF0YShhbGxfbm9zYilbWyJiYXRjaCJdXSA8LSBjb2xEYXRhKGFsbF9ub3NiKVtbInN0YWdlIl1dCmFsbF9ub3NiIDwtIHN1YnNldF9zZShhbGxfbm9zYiwgc3Vic2V0ID0gImNvbmRpdGlvbiE9J25vbmUnIikKYWxsX25vcm0gPC0gbm9ybWFsaXplKGFsbF9ub3NiLCBjb252ZXJ0ID0gImNwbSIsIG5vcm0gPSAicXVhbnQiLAogICAgICAgICAgICAgICAgICAgICAgdHJhbnNmb3JtID0gImxvZzIiLCBmaWx0ZXIgPSBUUlVFKQpwcm9fYW1hX3BjYSA8LSBwbG90X3BjYShhbGxfbm9ybSkKcHJvX2FtYV9wY2FbWyJwbG90Il1dCmBgYAoKSSB0aGluayB0aGUgYWJvdmUgcGljdHVyZSBpcyBzb3J0IG9mIHRoZSBvcHBvc2l0ZSBvZiB3aGF0IHdlIHdhbnQgdG8KY29tcGFyZSBpbiBhIERFIGFuYWx5c2lzIGZvciB0aGlzIHNldCBvZiBkYXRhLCBlLmcuIHdlIHdhbnQgdG8gY29tcGFyZQpwcm9tYXN0aWdvdGVzIGZyb20gYW1hc3RpZ290ZXM/CgpgYGB7cn0KdHdvX25vc2IgPC0gc2V0X2JhdGNoZXMoYWxsX25vc2IsIGZhY3QgPSAiY29uZGl0aW9uIikgJT4lCiAgc2V0X2NvbmRpdGlvbnMoZmFjdCA9ICJzdGFnZSIpICU+JQogIHN1YnNldF9zZShzdWJzZXQgPSAiYmF0Y2g9PSd6Mi4yJ3xiYXRjaD09J3oyLjMnIikKCnR3b19ub3JtIDwtIG5vcm1hbGl6ZSh0d29fbm9zYiwgY29udmVydCA9ICJjcG0iLCBub3JtID0gInF1YW50IiwKICAgICAgICAgICAgICAgICAgICAgIHRyYW5zZm9ybSA9ICJsb2cyIiwgZmlsdGVyID0gVFJVRSkKcHJvX2FtYV90d29fcGNhIDwtIHBsb3RfcGNhKHR3b19ub3JtKQpwcm9fYW1hX3R3b19wY2FbWyJwbG90Il1dCgp6eV9zdGFnZV9mYWN0b3IgPC0gcGFzdGUwKGNvbERhdGEodHdvX25vc2IpW1siYmF0Y2giXV0sICJfIiwKICAgICAgICAgICAgICAgICAgICAgICAgICBjb2xEYXRhKHR3b19ub3NiKVtbInN0YWdlIl1dKQpjb2xEYXRhKHR3b19ub3NiKVtbInp5c3RhZ2UiXV0gPC0genlfc3RhZ2VfZmFjdG9yCnp5c3RhZ2UgPC0gc2V0X2NvbmRpdGlvbnModHdvX25vc2IsIGZhY3QgPSAienlzdGFnZSIpCgp6eXN0YWdlX25vcm0gPC0gbm9ybWFsaXplKHp5c3RhZ2UsIGZpbHRlciA9IFRSVUUsIG5vcm0gPSAicXVhbnQiLAogICAgICAgICAgICAgICAgICAgICAgICAgIGNvbnZlcnQgPSAiY3BtIiwgdHJhbnNmb3JtID0gImxvZzIiKQpwbG90X3BjYSh6eXN0YWdlX25vcm0pJHBsb3QKCnp5c3RhZ2Vfa2VlcGVycyA8LSBsaXN0KAogICJ6MjMyMl9hbWEiID0gYygiejIzX2FtYXN0aWdvdGUiLCAiejIyX2FtYXN0aWdvdGUiKSwKICAiejIzMjJfcHJvIiA9IGMoInoyM19wcm9tYXN0aWdvdGUiLCAiejIyX3Byb21hc3RpZ290ZSIpLAogICJwcm9hbWFfejIzIiA9IGMoInoyM19hbWFzdGlnb3RlIiwgInoyM19wcm9tYXN0aWdvdGUiKSwKICAicHJvYW1hX3oyMiIgPSBjKCJ6MjJfYW1hc3RpZ290ZSIsICJ6MjJfcHJvbWFzdGlnb3RlIikpCgp6eXN0YWdlX2RlIDwtIGFsbF9wYWlyd2lzZSh6eXN0YWdlLCBmaWx0ZXIgPSBUUlVFLCBtb2RlbF9iYXRjaCA9ICJzdmFzZXEiLAogICAgICAgICAgICAgICAgICAgICAgICAgICBtb2RlbF9mc3RyaW5nID0gIn4gMCArIGNvbmRpdGlvbiIpCgp6eXN0YWdlX3RhYmxlcyA8LSBjb21iaW5lX2RlX3RhYmxlcygKICB6eXN0YWdlX2RlLCBrZWVwZXJzID0genlzdGFnZV9rZWVwZXJzLAogIGV4Y2VsID0gZ2x1ZSgiZXhjZWwvenltb2RlbWVfc3RhZ2VfdGFibGUtdnt2ZXJ9Lnhsc3giKSkKYGBgCgojIEdlbmUgZXhwcmVzc2lvbiB3aXRoIHJlc3BlY3QgdG8gY2hyb21vc29tZQoKSSB3YW50IHRvIG1ha2UgYSBwbG90IHdoZXJlIHRoZSB4LWF4aXMgaXMgdGhlIG51bWJlciBvZiBnZW5lcyBvbiBhIGNocm9tb3NvbWUgYW5kIHRoZQp5LWF4aXMgaXMgdGhlIG1lYW4gb2YgdGhlIGV4cHJlc3Npb24gb2YgdGhvc2UgZ2VuZXMuCgpgYGB7cn0KYXNzYXlfYnlfY2hyX3Bsb3QgPC0gcGxvdF9hc3NheV9ieV9jaHJvbW9zb21lKGxwX3p5bW8sIGNocm9tb3NvbWVfY29sdW1uID0gImNocm9tb3NvbWUiKQphc3NheV9ieV9jaHJfcGxvdFtbInBsb3QiXV0KYGBgCgojIFNOUCBwcm9maWxlcwoKT25lIHBvdGVudGlhbGx5IGludGVyZXN0aW5nIGFzcGVjdCBvZiB0aGUgdmFyaWFudCBkYXRhOiBpdCBtYXkgYmUgYWJsZQp0byBoZWxwIHVzIGRlZmluZSB0aGUgenltb2RlbWUgc3RhdGUgb2YgcHJldmlvdXMsIHVudGVzdGVkIHNhbXBsZXMuCgpJbiBvcmRlciB0byB0ZXN0IHRoaXMsIEkgYW0gbG9hZGluZyBzb21lIG9mIHRoZSAyMDE2IGRhdGEgYWxvbmdzaWRlCnRoZSBuZXcgVE1SQzIgZGF0YSB0byBzZWUgaWYgdGhleSBmaXQgdG9nZXRoZXIuCgpUaGlzIGlzIHVzaW5nIGFuIG9sZGVyIGRhdGFzZXQgZm9yIHdoaWNoIEkgYW0gbm90IHN1cmUgd2UgaGF2ZQpwZXJtaXNzaW9ucyB0byBpbmNsdWRlIGluIHRoZSBjb250YWluZXIsIHNvIEkgYW0gdHVybmluZyB0aGVtIG9mZiBmb3IKbm93LgoKYGBge3IsIGV2YWw9RkFMU0V9Cm9sZF9zZSA8LSBjcmVhdGVfc2UoInNhbXBsZV9zaGVldHMvdG1yYzJfc2FtcGxlc18yMDE5MTIwMy54bHN4IiwKICAgICAgICAgICAgICAgICAgICAgICAgZmlsZV9jb2x1bW4gPSAidG9waGF0MmZpbGUiKQoKdHQgPC0gb2xkX3NlJGV4cHJlc3Npb25zZXQKcm93bmFtZXModHQpIDwtIGdzdWIocGF0dGVybiA9ICJeZXhvbl8iLCByZXBsYWNlbWVudCA9ICIiLCB4ID0gcm93bmFtZXModHQpKQpyb3duYW1lcyh0dCkgPC0gZ3N1YihwYXR0ZXJuID0gIlxcLjEkIiwgcmVwbGFjZW1lbnQgPSAiIiwgeCA9IHJvd25hbWVzKHR0KSkKb2xkX3NlJGV4cHJlc3Npb25zZXQgPC0gdHQKcm0odHQpCmBgYAoKIyMgQ3JlYXRlIHRoZSBTTlAgZXhwcmVzc2lvbnNldAoKT25lIG90aGVyIGltcG9ydGFudCBjYXZlYXQsIHdlIGhhdmUgYSBncm91cCBvZiBuZXcgc2FtcGxlcyB3aGljaCBoYXZlCm5vdCB5ZXQgcnVuIHRocm91Z2ggdGhlIHZhcmlhbnQgc2VhcmNoIHBpcGVsaW5lLCBzbyBJIG5lZWQgdG8gcmVtb3ZlCnRoZW0gZnJvbSBjb25zaWRlcmF0aW9uLiAgVGhvdWdoIGl0IGxvb2tzIGxpa2UgdGhleSBmaW5pc2hlZCBvdmVybmlnaHQuLi4KCkluIHRoZSBub24tY29udGFpbmVyaXplZCB2ZXJzaW9uIG9mIHRoaXMgZG9jdW1lbnQsIHRoZSBmb2xsb3dpbmcgYmxvY2sKY29tYmluZXMgYW4gb2xkZXIgZGF0YXNldCB3aXRoIHRoZSBjdXJyZW50IGRhdGEuCgpgYGB7cn0KYm90aF9ub3JtIDwtIG5vcm1hbGl6ZShuZXdfc25wc19zdWZmaWNpZW50LCB0cmFuc2Zvcm0gPSAibG9nMiIsIG5vcm0gPSAicXVhbnQiKSAlPiUKICBzZXRfY29uZGl0aW9ucyhmYWN0ID0gInBhdGhvZ2Vuc3RyYWluIikKYGBgCgpUaGUgZGF0YSBzdHJ1Y3R1cmUgJ2JvdGhfbm9ybScgbm93IGNvbnRhaW5zIG91ciAyMDE2IGRhdGEgYWxvbmcgd2l0aAp0aGUgbmV3ZXIgZGF0YSBjb2xsZWN0ZWQgc2luY2UgMjAxOS4KCiMjIFBsb3Qgb2YgU05QIHByb2ZpbGVzIGZvciB6eW1vZGVtZXMKClRoZSBmb2xsb3dpbmcgcGxvdCBzaG93cyB0aGUgU05QIHByb2ZpbGVzIG9mIGFsbCBzYW1wbGVzIChvbGQgYW5kIG5ldykgd2hlcmUgdGhlCmNvbG9ycyBhdCB0aGUgdG9wIHNob3cgZWl0aGVyIHRoZSAyLjIgc3RyYWlucyAob3JhbmdlKSwgMi4zIHN0cmFpbnMgKGdyZWVuKSwgdGhlCnByZXZpb3VzIHNhbXBsZXMgKHB1cnBsZSksIG9yIHRoZSB2YXJpb3VzIGxhYiBzdHJhaW5zIChwaW5rIGV0YykuCgpgYGB7cn0KbmV3X3ZhcmlhbnRfaGVhdG1hcCA8LSBwbG90X2Rpc2hlYXQobmV3X3NucHNfc3VmZmljaWVudCkKZGV2IDwtIHBwKGZpbGUgPSAiaW1hZ2VzL3Jhd19zbnBfZGlzaGVhdC5wbmciLCBoZWlnaHQgPSAxMiwgd2lkdGggPSAxMikKbmV3X3ZhcmlhbnRfaGVhdG1hcCRwbG90CmNsb3NlZCA8LSBkZXYub2ZmKCkKbmV3X3ZhcmlhbnRfaGVhdG1hcCRwbG90CmBgYAoKVGhlIGZ1bmN0aW9uIGdldF9zbnBfc2V0cygpIHRha2VzIHRoZSBwcm92aWRlZCBtZXRhZGF0YSBmYWN0b3IgKGluCnRoaXMgY2FzZSAnY29uZGl0aW9uJykgYW5kIGxvb2tzIGZvciB2YXJpYW50cyB3aGljaCBhcmUgZXhjbHVzaXZlIHRvCmVhY2ggZWxlbWVudCBpbiBpdC4gIEluIHRoaXMgY2FzZSwgdGhpcyBpcyBsb29raW5nIGZvciBkaWZmZXJlbmNlcwpiZXR3ZWVuIDIuMiBhbmQgMi4zLCBhcyB3ZWxsIGFzIHRoZSBzZXQgc2hhcmVkIGFtb25nIHRoZW0uCgpgYGB7cn0Kc25wX3NldHMgPC0gZ2V0X3NucF9zZXRzKG5ld19zbnBzX3N1ZmZpY2llbnQsIGZhY3RvciA9ICJjb25kaXRpb24iKQpzbnBfc2V0cwojI0Jpb2Jhc2U6OmFubm90YXRpb24ob2xkX3NlJGV4cHJlc3Npb25zZXQpID0gQmlvYmFzZTo6YW5ub3RhdGlvbihscF9zZSRleHByZXNzaW9uc2V0KQojI2JvdGhfc2UgPC0gY29tYmluZV9zZXMobHBfc2UsIG9sZF9zZSkKCnNucF9nZW5lcyA8LSBzbnBzX3ZzX2dlbmVzKGxwX3NlLCBzbnBfc2V0cywgY2hyX2NvbHVtbiA9IGV4cF9jaHJfY29sLAogICAgICAgICAgICAgICAgICAgICAgICAgICBzdGFydF9jb2x1bW4gPSBleHBfc3RhcnRfY29sLCBlbmRfY29sdW1uID0gZXhwX2VuZF9jb2wpCiMjIEkgdGhpbmsgd2UgaGF2ZSBzb21lIG1ldHJpY3MgaGVyZSB3ZSBjYW4gcGxvdC4uLgpzbnBfc3Vic2V0IDwtIHNucF9zdWJzZXRfZ2VuZXMoCiAgbHBfc2UsIG5ld19zbnBzX3N1ZmZpY2llbnQsIHN0YXJ0X2NvbHVtbiA9IGV4cF9zdGFydF9jb2wsIGVuZF9jb2x1bW4gPSBleHBfZW5kX2NvbCwKICBleHBfbmFtZV9jb2x1bW4gPSBleHBfY2hyX2NvbCwKICBnZW5lcyA9IGMoIkxQQUwxM18xMjAwMTA5MDAiLCAiTFBBTDEzXzM0MDAxMzAwMCIsICJMUEFMMTNfMDAwMDU0MTAwIiwKICAgICAgICAgICAgIkxQQUwxM18xNDAwMDYxMDAiLCAiTFBBTDEzXzE4MDAxODUwMCIsICJMUEFMMTNfMzIwMDIyMzAwIikpCnR0IDwtIG5vcm1hbGl6ZShzbnBfc3Vic2V0LCB0cmFuc2Zvcm0gPSAibG9nMiIsIGZpbHRlciA9IFRSVUUpCnp5bW9faGVhdCA8LSBwbG90X3NhbXBsZV9oZWF0bWFwKHR0LCByb3dfbGFiZWwgPSByb3duYW1lcyhhc3NheShzbnBfc3Vic2V0KSkpCnp5bW9faGVhdApgYGAKCiMjIENvbXBhcmUgdmFyaWFudHMgdG8gREUgZ2VuZXMKCk5hamliIGhhcyBhc2tlZCBhIGZldyB0aW1lcyBhYm91dCB0aGUgcmVsYXRpb25zaGlwIGJldHdlZW4gdmFyaWFudHMKYW5kIERFIGdlbmVzLiAgSW4gc3Vic2VxdWVudCBjb252ZXJzYXRpb25zIEkgZmlndXJlZCBvdXQgd2hhdCBoZQpyZWFsbHkgd2FudHMgdG8gbGVhcm4gaXMgdmFyaWFudHMgaW4gdGhlIFVUUiAobW9zdCBsaWtlbHkgNScpIHdoaWNoCm1pZ2h0IGFmZmVjdCBleHByZXNzaW9uIG9mIGdlbmVzLiAgVGhlIGZvbGxvd2luZyBleHBsaWNpdGx5IGRvZXMgbm90CmhlbHAgdGhpcyBxdWVzdGlvbiwgYnV0IGlzIGEgcGFyYWxvZzogaXMgdGhlcmUgYSByZWxhdGlvbnNoaXAgYmV0d2Vlbgp2YXJpYW50cyBpbiB0aGUgQ0RTIGFuZCBkaWZmZXJlbnRpYWwgZXhwcmVzc2lvbj8KCiMjIyBDb2xsZWN0IERFIGRhdGEKCkluIG9yZGVyIHRvIGRvIHRoaXMgY29tcGFyaXNvbiwgd2UgbmVlZCB0byByZWxvYWQgc29tZSBvZiB0aGUgREUgcmVzdWx0cy4KClRoZXNlIGJsb2NrcyBuZWVkIHRvIGJlIG1vdmVkIHRvIHBvc3QtZGlmZmVyZW50aWFsIGFuYWx5c2VzCgpgYGB7ciByZWxvYWRfZGVfcmVzdWx0cywgZXZhbD1GQUxTRX0KcmRhIDwtIGdsdWUoInJkYS96eW1vX3RhYmxlc19zdmEtdnt2ZXJ9LnJkYSIpCnZhcm5hbWUgPC0gZ3N1Yih4ID0gYmFzZW5hbWUocmRhKSwgcGF0dGVybiA9ICJcXC5yZGEiLCByZXBsYWNlbWVudCA9ICIiKQpsb2FkZWQgPC0gbG9hZChmaWxlID0gcmRhKQp6eV9kZiA8LSBnZXQwKHZhcm5hbWUpW1siZGF0YSJdXVtbInp5bW9kZW1lIl1dCmBgYAoKYGBge3IgdmFyaWFudHNfdnNfZGUsIGV2YWw9RkFMU0V9CnZhcnNfZGYgPC0gZGF0YS5mcmFtZShJRCA9IG5hbWVzKHNucF9nZW5lcyRzdW1tYXJ5X2J5X2dlbmUpLAogICAgICAgICAgICAgICAgICAgICAgdmFyaWFudHMgPSBhcy5udW1lcmljKHNucF9nZW5lcyRzdW1tYXJ5X2J5X2dlbmUpKQp2YXJzX2RmW1sidmFyaWFudHMiXV0gPC0gbG9nMih2YXJzX2RmW1sidmFyaWFudHMiXV0gKyAxKQp2YXJzX2J5X2RlX2dlbmUgPC0gbWVyZ2UoenlfZGYsIHZhcnNfZGYsIGJ5LnggPSAicm93Lm5hbWVzIiwgYnkueSA9ICJJRCIpCmNvci50ZXN0KHZhcnNfYnlfZGVfZ2VuZSRkZXNlcV9sb2dmYywgdmFyc19ieV9kZV9nZW5lJHZhcmlhbnRzKQp2YXJpYW50c193cnRfbG9nZmMgPC0gcGxvdF9saW5lYXJfc2NhdHRlcih2YXJzX2J5X2RlX2dlbmVbLCBjKCJkZXNlcV9sb2dmYyIsICJ2YXJpYW50cyIpXSkKdmFyaWFudHNfd3J0X2xvZ2ZjJHNjYXR0ZXIKIyMgSXQgbG9va3MgbGlrZSB0aGVyZSBtaWdodCBiZSBzb21lIGdlbmVzIG9mIGludGVyZXN0LCBldmVuIHRob3VnaCB0aGlzIGlzIG5vdCBhY3R1YWxseQojIyB0aGUgcXVlc3Rpb24gb2YgaW50ZXJlc3QuCmBgYAoKRGlkbid0IEkgY3JlYXRlIGEgc2V0IG9mIGRlbnNpdGllcyBieSBjaHJvbW9zb21lPwpPaCBJIHRoaW5rIHRoZXkgY29tZSBpbiBmcm9tIGdldF9zbnBfc2V0cygpCgojIyBTTlBTIGFzc29jaWF0ZWQgd2l0aCBjbGluaWNhbCByZXNwb25zZSBpbiB0aGUgVE1SQyBzYW1wbGVzCgpgYGB7cn0KY2xpbmljYWxfc2V0cyA8LSBnZXRfc25wX3NldHMobmV3X3NucHNfc3VmZmljaWVudCwgZmFjdG9yID0gImNsaW5pY2FscmVzcG9uc2UiKQpjbGluaWNhbF9zZXRzCgpkZW5zaXR5X3ZlYyA8LSBjbGluaWNhbF9zZXRzW1siZGVuc2l0eSJdXQpjaHJvbW9zb21lX2lkeCA8LSBncmVwKHBhdHRlcm4gPSAiTHBhTCIsIHggPSBuYW1lcyhkZW5zaXR5X3ZlYykpCmRlbnNpdHlfZGYgPC0gYXMuZGF0YS5mcmFtZShkZW5zaXR5X3ZlY1tjaHJvbW9zb21lX2lkeF0pCmRlbnNpdHlfZGZbWyJjaHIiXV0gPC0gcm93bmFtZXMoZGVuc2l0eV9kZikKY29sbmFtZXMoZGVuc2l0eV9kZikgPC0gYygiZGVuc2l0eV92ZWMiLCAiY2hyIikKdmFyX2Rlbl9jaHIgPC0gZ2dwbG90KGRlbnNpdHlfZGYsIGFlcyh4ID0gY2hyLCB5ID0gZGVuc2l0eV92ZWMpKSArCiAgZ2dwbG90Mjo6Z2VvbV9jb2woKSArCiAgZ2dwbG90Mjo6dGhlbWUoYXhpcy50ZXh0ID0gZ2dwbG90Mjo6ZWxlbWVudF90ZXh0KHNpemUgPSAxMCwgY29sb3VyID0gImJsYWNrIiksCiAgICAgICAgICAgICAgICAgYXhpcy50ZXh0LnggPSBnZ3Bsb3QyOjplbGVtZW50X3RleHQoYW5nbGUgPSA5MCwgdmp1c3QgPSAwLjUpKQp2YXJfZGVuX2NocgpwcChmaWxlID0gImZpZ3VyZXMvdmFyaWFudF9kZW5zaXR5X2J5X2Nocm9tb3NvbWUucGRmIikKdmFyX2Rlbl9jaHIKZGV2Lm9mZigpCiMjIG9vcHMsIGZvcmdvdCB0byBleHBvcnQgd3JpdGVfc25wcy4uLiAgZml4ZWQuCmNsaW5pY2FsX3dyaXR0ZW4gPC0gd3JpdGVfc25wcyhuZXdfc25wc19zdWZmaWNpZW50LCBvdXRwdXRfZmlsZSA9ICJleGNlbC9jbGluaWNhbF92YXJpYW50cy5hbG4iKQpgYGAKCiMjIyBDcm9zcyByZWZlcmVuY2UgdGhlc2UgdmFyaWFudHMgYnkgZ2VuZQoKYGBge3J9YwpjbGluaWNhbF9nZW5lcyA8LSBzbnBzX3ZzX2dlbmVzKGxwX3NlLCBjbGluaWNhbF9zZXRzLCBjaHJfY29sdW1uID0gZXhwX2Nocl9jb2wsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc3RhcnRfY29sdW1uID0gZXhwX3N0YXJ0X2NvbCwgZW5kX2NvbHVtbiA9IGV4cF9lbmRfY29sKQoKc25wX2RlbnNpdHkgPC0gbWVyZ2UoYXMuZGF0YS5mcmFtZShjbGluaWNhbF9nZW5lc1tbInN1bW1hcnkiXV0pLAogICAgICAgICAgICAgICAgICAgICBhcy5kYXRhLmZyYW1lKHJvd0RhdGEobHBfc2UpKSwKICAgICAgICAgICAgICAgICAgICAgYnkgPSAicm93Lm5hbWVzIikKc25wX2RlbnNpdHkgPC0gc25wX2RlbnNpdHlbLCBjKDEsIDIsIDQsIDE1KV0KY29sbmFtZXMoc25wX2RlbnNpdHkpIDwtIGMoIm5hbWUiLCAic25wcyIsICJwcm9kdWN0IiwgImxlbmd0aCIpCnNucF9kZW5zaXR5W1sicHJvZHVjdCJdXSA8LSB0b2xvd2VyKHNucF9kZW5zaXR5W1sicHJvZHVjdCJdXSkKc25wX2RlbnNpdHlbWyJsZW5ndGgiXV0gPC0gYXMubnVtZXJpYyhzbnBfZGVuc2l0eVtbImxlbmd0aCJdXSkKc25wX2RlbnNpdHlbWyJkZW5zaXR5Il1dIDwtIGFzLm51bWVyaWMoc25wX2RlbnNpdHlbWyJzbnBzIl1dKSAvIHNucF9kZW5zaXR5W1sibGVuZ3RoIl1dCnNucF9pZHggPC0gb3JkZXIoc25wX2RlbnNpdHlbWyJkZW5zaXR5Il1dLCBkZWNyZWFzaW5nID0gVFJVRSkKc25wX2RlbnNpdHkgPC0gc25wX2RlbnNpdHlbc25wX2lkeCwgXQoKcmVtb3ZlcnMgPC0gYygiYW1hc3RpbiIsICJncDYzIiwgImxlaXNobWFub2x5c2luIikKZm9yIChyIGluIHJlbW92ZXJzKSB7CiAgZHJvcF9pZHggPC0gZ3JlcGwocGF0dGVybiA9IHIsIHggPSBzbnBfZGVuc2l0eVtbInByb2R1Y3QiXV0pCiAgc25wX2RlbnNpdHkgPC0gc25wX2RlbnNpdHlbIWRyb3BfaWR4LCBdCn0KIyMgRmlsdGVyIHRoZXNlIGZvciBbQXxhXW1hc3RpbiBncDYzIExlaXNobWFub2x5c2luCmBgYAoKTGV0IHVzIGdyYWIgb3V0IHRoZSBudW1iZXIgb2YgdmFyaWFudHMvZ2VuZSBmb3IgdGhlIGN1cmUvZmFpbCBzYW1wbGVzLAptZXJnZSB0aGVtIGludG8gYSBkYXRhZnJhbWUsIGFuZCBhZGQgdGhhdCB0byB0aGUgZ2VuZSBhbm5vdGF0aW9ucyBmb3IKdGhlIGxwX3NlIGRhdGFzdHJ1Y3R1cmUuCgpgYGB7cn0KY2xpbmljYWxfc25wcyA8LSBzbnBzX2ludGVyc2VjdGlvbnMobHBfc2UsIGNsaW5pY2FsX3NldHMsIGNocl9jb2x1bW4gPSBleHBfY2hyX2NvbCwgc3RhcnRfY29sdW1uID0gZXhwX3N0YXJ0X2NvbCwgZW5kX2NvbHVtbiA9IGV4cF9lbmRfY29sKQoKZmFpbF9yZWZfc25wcyA8LSBhcy5kYXRhLmZyYW1lKGNsaW5pY2FsX3NucHNbWyJpbnRlcnMiXV1bWyJmYWlsdXJlLCByZWZlcmVuY2Ugc3RyYWluIl1dKQpmYWlsX3JlZl9zbnBzIDwtIHJiaW5kKGZhaWxfcmVmX3NucHMsCiAgICAgICAgICAgICAgICAgICAgICAgYXMuZGF0YS5mcmFtZShjbGluaWNhbF9zbnBzW1siaW50ZXJzIl1dW1siZmFpbHVyZSJdXSkpCmN1cmVfc25wcyA8LSBhcy5kYXRhLmZyYW1lKGNsaW5pY2FsX3NucHNbWyJpbnRlcnMiXV1bWyJjdXJlIl1dKQoKaGVhZChmYWlsX3JlZl9zbnBzKQpoZWFkKGN1cmVfc25wcykKd3JpdGUuY3N2KGZpbGUgPSAiZXhjZWwvY3VyZV92YXJpYW50cy50eHQiLCB4ID0gcm93bmFtZXMoY3VyZV9zbnBzKSkKd3JpdGUuY3N2KGZpbGUgPSAiZXhjZWwvZmFpbF92YXJpYW50cy50eHQiLCB4ID0gcm93bmFtZXMoZmFpbF9yZWZfc25wcykpCgphbm5vdCA8LSByb3dEYXRhKGxwX3NlKQpjbGluaWNhbF9pbnRlcmVzdF9jdXJlIDwtIGFzLmRhdGEuZnJhbWUoY2xpbmljYWxfc25wc1tbImdlbmVfc3VtbWFyaWVzIl1dW1siY3VyZSJdXSkKc3VtbWFyeShhcy5mYWN0b3IoY2xpbmljYWxfaW50ZXJlc3RfY3VyZVtbMV1dKSkKY2xpbmljYWxfaW50ZXJlc3RfZmFpbCA8LSBhcy5kYXRhLmZyYW1lKGNsaW5pY2FsX3NucHNbWyJnZW5lX3N1bW1hcmllcyJdXVtbImZhaWx1cmUiXV0pCnN1bW1hcnkoYXMuZmFjdG9yKGNsaW5pY2FsX2ludGVyZXN0X2ZhaWxbWzFdXSkpCgpjbGluaWNhbF9pbnRlcmVzdCA8LSBtZXJnZShjbGluaWNhbF9pbnRlcmVzdF9jdXJlLAogICAgICAgICAgICAgICAgICAgICAgICAgICBjbGluaWNhbF9pbnRlcmVzdF9mYWlsLAogICAgICAgICAgICAgICAgICAgICAgICAgICBieSA9ICJyb3cubmFtZXMiLCBhbGwgPSBUUlVFKQoKcm93bmFtZXMoY2xpbmljYWxfaW50ZXJlc3QpIDwtIGNsaW5pY2FsX2ludGVyZXN0W1siUm93Lm5hbWVzIl1dCmNsaW5pY2FsX2ludGVyZXN0W1siUm93Lm5hbWVzIl1dIDwtIE5VTEwKY29sbmFtZXMoY2xpbmljYWxfaW50ZXJlc3QpIDwtIGMoImN1cmVfc25wcyIsICJmYWlsX3NucHMiKQpjbGluaWNhbF9hbm5vdCA8LSBtZXJnZShhbm5vdCwgY2xpbmljYWxfaW50ZXJlc3QsIGJ5ID0gInJvdy5uYW1lcyIpCnJvd25hbWVzKGFubm90KSA8LSBhbm5vdFtbIlJvdy5uYW1lcyJdXQphbm5vdFtbIlJvdy5uYW1lcyJdXSA8LSBOVUxMCmRpbShhbm5vdCkKZGltKHJvd0RhdGEobHBfc2UpKQpyb3dEYXRhKGxwX3NlKSA8LSBhbm5vdApgYGAKCiMgWnltb2RlbWUgZm9yIG5ldyBzYW1wbGVzCgpUaGUgaGVhdG1hcCBwcm9kdWNlZCBoZXJlIHNob3VsZCBzaG93IHRoZSB2YXJpYW50cyBvbmx5IGZvciB0aGUgenltb2RlbWUgZ2VuZXMuCgojIyBIdW50IGZvciBzbnAgY2x1c3RlcnMKCkkgYW0gdGhpbmtpbmcgdGhhdCBpZiB3ZSBmaW5kIGNsdXN0ZXJzIG9mIGxvY2F0aW9ucyB3aGljaCBhcmUgdmFyaWFudCwgdGhhdAptaWdodCBwcm92aWRlIHNvbWUgUENSIHRlc3RpbmcgcG9zc2liaWxpdGllcy4KCmBgYHtyfQojIyBEcm9wIHRoZSAyLjEsIDIuNCwgdW5rbm93biwgYW5kIG51bGwKcHJ1bmVkX3NucHMgPC0gc3Vic2V0X3NlKG5ld19zbnBzX3N1ZmZpY2llbnQsIHN1YnNldCA9ICJjb25kaXRpb249PSd6Mi4yJ3xjb25kaXRpb249PSd6Mi4zJyIpCm5ld19zZXRzIDwtIGdldF9zbnBfc2V0cyhwcnVuZWRfc25wcywgZmFjdG9yID0gInp5bW9kZW1lY2F0ZWdvcmljYWwiKQpzdW1tYXJ5KG5ld19zZXRzKQojIyAxMDAwMDAwOiAyLjIKIyMgMDEwMDAwMDogMi4zCgpzdW1tYXJ5KG5ld19zZXRzW1siaW50ZXJzZWN0aW9ucyJdXVtbIjEwIl1dKQp3cml0ZS5jc3YoZmlsZSA9ICJleGNlbC92YXJpYW50c18yMi5jc3YiLCB4ID0gbmV3X3NldHNbWyJpbnRlcnNlY3Rpb25zIl1dW1siMTAiXV0pCnN1bW1hcnkobmV3X3NldHNbWyJpbnRlcnNlY3Rpb25zIl1dW1siMDEiXV0pCndyaXRlLmNzdihmaWxlID0gImV4Y2VsL3ZhcmlhbnRzXzIzLmNzdiIsIHggPSBuZXdfc2V0c1tbImludGVyc2VjdGlvbnMiXV1bWyIwMSJdXSkKYGBgCgpUaHVzIHdlIHNlZSB0aGF0IHRoZXJlIGFyZSAzLDU1MyB2YXJpYW50cyBhc3NvY2lhdGVkIHdpdGggMi4yIGFuZAo4MSw1ODkgYXNzb2NpYXRlZCB3aXRoIDIuMy4KCiMjIyBBIHNtYWxsIGZ1bmN0aW9uIGZvciBzZWFyY2hpbmcgZm9yIHBvdGVudGlhbCBQQ1IgcHJpbWVycwoKVGhlIGZvbGxvd2luZyBmdW5jdGlvbiB1c2VzIHRoZSBwb3NpdGlvbmFsIGRhdGEgdG8gbG9vayBmb3Igc2VxdWVudGlhbAptaXNtYXRjaGVzIGFzc29jaWF0ZWQgd2l0aCB6eW1vZGVtZSBpbiB0aGUgaG9wZXMgdGhhdCB0aGVyZSB3aWxsIGJlCnNvbWUgcmVnaW9ucyB3aGljaCB3b3VsZCBwcm92aWRlIGdvb2QgcG90ZW50aWFsIHRhcmdldHMgZm9yIGEKUENSLWJhc2VkIGFzc2F5LgoKYGBge3Igc2VxdWVudGlhbF9zZWFyY2gsIGV2YWw9RkFMU0V9CnNlcXVlbnRpYWxfdmFyaWFudHMgPC0gZnVuY3Rpb24oc25wX3NldHMsIGNvbmRpdGlvbnMgPSBOVUxMLCBtaW5pbXVtID0gMywgbWF4aW11bV9zZXBhcmF0aW9uID0gMykgewogIGlmIChpcy5udWxsKGNvbmRpdGlvbnMpKSB7CiAgICBjb25kaXRpb25zIDwtIDEKICB9CiAgaW50ZXJzZWN0aW9uX3NldHMgPC0gc25wX3NldHNbWyJpbnRlcnNlY3Rpb25zIl1dCiAgaW50ZXJzZWN0aW9uX25hbWVzIDwtIHNucF9zZXRzW1sic2V0X25hbWVzIl1dCiAgY2hvc2VuX2ludGVyc2VjdGlvbiA8LSAxCiAgaWYgKGlzLm51bWVyaWMoY29uZGl0aW9ucykpIHsKICAgIGNob3Nlbl9pbnRlcnNlY3Rpb24gPC0gY29uZGl0aW9ucwogIH0gZWxzZSB7CiAgICBpbnRlcnNlY3Rpb25faWR4IDwtIGludGVyc2VjdGlvbl9uYW1lcyA9PSBjb25kaXRpb25zCiAgICBjaG9zZW5faW50ZXJzZWN0aW9uIDwtIG5hbWVzKGludGVyc2VjdGlvbl9uYW1lcylbaW50ZXJzZWN0aW9uX2lkeF0KICB9CgogIHBvc3NpYmxlX3Bvc2l0aW9ucyA8LSBpbnRlcnNlY3Rpb25fc2V0c1tbY2hvc2VuX2ludGVyc2VjdGlvbl1dCiAgcG9zaXRpb25fdGFibGUgPC0gZGF0YS5mcmFtZShyb3cubmFtZXMgPSBwb3NzaWJsZV9wb3NpdGlvbnMpCiAgcGF0IDwtICJeY2hyXyguKylfcG9zXyguKylfcmVmXy4qJCIKICBwb3NpdGlvbl90YWJsZVtbImNociJdXSA8LSBnc3ViKHBhdHRlcm4gPSBwYXQsIHJlcGxhY2VtZW50ID0gIlxcMSIsIHggPSByb3duYW1lcyhwb3NpdGlvbl90YWJsZSkpCiAgcG9zaXRpb25fdGFibGVbWyJwb3MiXV0gPC0gYXMubnVtZXJpYyhnc3ViKHBhdHRlcm4gPSBwYXQsIHJlcGxhY2VtZW50ID0gIlxcMiIsIHggPSByb3duYW1lcyhwb3NpdGlvbl90YWJsZSkpKQogIHBvc2l0aW9uX2lkeCA8LSBvcmRlcihwb3NpdGlvbl90YWJsZVssICJjaHIiXSwgcG9zaXRpb25fdGFibGVbLCAicG9zIl0pCiAgcG9zaXRpb25fdGFibGUgPC0gcG9zaXRpb25fdGFibGVbcG9zaXRpb25faWR4LCBdCiAgcG9zaXRpb25fdGFibGVbWyJkaXN0Il1dIDwtIDAKCiAgbGFzdF9jaHIgPC0gIiIKICBmb3IgKHIgaW4gMTpucm93KHBvc2l0aW9uX3RhYmxlKSkgewogICAgdGhpc19jaHIgPC0gcG9zaXRpb25fdGFibGVbciwgImNociJdCiAgICBpZiAociA9PSAxKSB7CiAgICAgIHBvc2l0aW9uX3RhYmxlW3IsICJkaXN0Il0gPC0gcG9zaXRpb25fdGFibGVbciwgInBvcyJdCiAgICAgIGxhc3RfY2hyIDwtIHRoaXNfY2hyCiAgICAgIG5leHQKICAgIH0KICAgIGlmICh0aGlzX2NociA9PSBsYXN0X2NocikgewogICAgICBwb3NpdGlvbl90YWJsZVtyLCAiZGlzdCJdIDwtIHBvc2l0aW9uX3RhYmxlW3IsICJwb3MiXSAtIHBvc2l0aW9uX3RhYmxlW3IgLSAxLCAicG9zIl0KICAgIH0gZWxzZSB7CiAgICAgIHBvc2l0aW9uX3RhYmxlW3IsICJkaXN0Il0gPC0gcG9zaXRpb25fdGFibGVbciwgInBvcyJdCiAgICB9CiAgICBsYXN0X2NociA8LSB0aGlzX2NocgogIH0KCiAgIyMgV29ya2luZyBpbnRlcmFjdGl2ZWx5IGhlcmUuCgogIGRvdWJsZXMgPC0gcG9zaXRpb25fdGFibGVbWyJkaXN0Il1dID09IDEKICBkb3VibGVzIDwtIHBvc2l0aW9uX3RhYmxlW2RvdWJsZXMsIF0KICB3cml0ZS5jc3YoZG91YmxlcywgImRvdWJsZXMuY3N2IikKCiAgb25lX2F3YXkgPC0gcG9zaXRpb25fdGFibGVbWyJkaXN0Il1dID09IDIKICBvbmVfYXdheSA8LSBwb3NpdGlvbl90YWJsZVtvbmVfYXdheSwgXQogIHdyaXRlLmNzdihvbmVfYXdheSwgIm9uZV9hd2F5LmNzdiIpCgogIHR3b19hd2F5IDwtIHBvc2l0aW9uX3RhYmxlW1siZGlzdCJdXSA9PSAzCiAgdHdvX2F3YXkgPC0gcG9zaXRpb25fdGFibGVbdHdvX2F3YXksIF0KICB3cml0ZS5jc3YodHdvX2F3YXksICJ0d29fYXdheS5jc3YiKQoKICBjb21iaW5lZCA8LSByYmluZChkb3VibGVzLCBvbmVfYXdheSkKICBjb21iaW5lZCA8LSByYmluZChjb21iaW5lZCwgdHdvX2F3YXkpCiAgcG9zaXRpb25faWR4IDwtIG9yZGVyKGNvbWJpbmVkWywgImNociJdLCBjb21iaW5lZFssICJwb3MiXSkKICBjb21iaW5lZCA8LSBjb21iaW5lZFtwb3NpdGlvbl9pZHgsIF0KCiAgdGhpc19jaHIgPC0gIiIKICBmb3IgKHIgaW4gMTpucm93KGNvbWJpbmVkKSkgewogICAgdGhpc19jaHIgPC0gY29tYmluZWRbciwgImNociJdCiAgICBpZiAociA9PSAxKSB7CiAgICAgIGNvbWJpbmVkW3IsICJkaXN0X3BhaXIiXSA8LSBjb21iaW5lZFtyLCAicG9zIl0KICAgICAgbGFzdF9jaHIgPC0gdGhpc19jaHIKICAgICAgbmV4dAogICAgfQogICAgaWYgKHRoaXNfY2hyID09IGxhc3RfY2hyKSB7CiAgICAgIGNvbWJpbmVkW3IsICJkaXN0X3BhaXIiXSA8LSBjb21iaW5lZFtyLCAicG9zIl0gLSBjb21iaW5lZFtyIC0gMSwgInBvcyJdCiAgICB9IGVsc2UgewogICAgICBjb21iaW5lZFtyLCAiZGlzdF9wYWlyIl0gPC0gY29tYmluZWRbciwgInBvcyJdCiAgICB9CiAgICBsYXN0X2NociA8LSB0aGlzX2NocgogIH0KCiAgZGlzdF9wYWlyX21heGltdW0gPC0gMTAwMAogIGRpc3RfcGFpcl9taW5pbXVtIDwtIDIwMAogIGRpc3RfcGFpcl9pZHggPC0gY29tYmluZWRbWyJkaXN0X3BhaXIiXV0gPD0gZGlzdF9wYWlyX21heGltdW0gJgogICAgY29tYmluZWRbWyJkaXN0X3BhaXIiXV0gPj0gZGlzdF9wYWlyX21pbmltdW0KICByZW1haW5pbmcgPC0gY29tYmluZWRbZGlzdF9wYWlyX2lkeCwgXQogIG5vX3dlYWtfaWR4IDwtIGdyZXBsKHBhdHRlcm4gPSAicmVmXyhHfEMpIiwgeCA9IHJvd25hbWVzKHJlbWFpbmluZykpCiAgcmVtYWluaW5nIDwtIHJlbWFpbmluZ1tub193ZWFrX2lkeCwgXQoKICBwcmludChoZWFkKHRhYmxlKHBvc2l0aW9uX3RhYmxlW1siZGlzdCJdXSkpKQogIHNlcXVlbnRpYWxzIDwtIHBvc2l0aW9uX3RhYmxlW1siZGlzdCJdXSA8PSBtYXhpbXVtX3NlcGFyYXRpb24KICBtZXNzYWdlKCJUaGVyZSBhcmUgIiwgc3VtKHNlcXVlbnRpYWxzKSwgIiBjYW5kaWRhdGUgcmVnaW9ucy4iKQoKICAjIyBUaGUgZm9sbG93aW5nIGNhbiB0ZWxsIG1lIGhvdyBtYW55IHJ1bnMgb2YgZWFjaCBsZW5ndGggb2NjdXJyZWQsIHRoYXQgaXMgbm90IHF1aXRlIHdoYXQgSSB3YW50LgogICMjIE5vdyB1c2UgcnVuIGxlbmd0aCBlbmNvZGluZyB0byBmaW5kIHRoZSBzZXQgb2Ygc2VxdWVudGlhbCBzZXF1ZW50aWFscyEKICBybGVfcmVzdWx0IDwtIHJsZShzZXF1ZW50aWFscykKICBybGVfdmFsdWVzIDwtIHJsZV9yZXN1bHRbWyJ2YWx1ZXMiXV0KICAjIyBUaGUgZm9sbG93aW5nIGxpbmUgaXMgZXF1aXZhbGVudCB0byBqdXN0IGxlYXZpbmcgdmFsdWVzIGFsb25lOgogICMjIHRydWVfdmFsdWVzIDwtIHJsZV9yZXN1bHRbWyJ2YWx1ZXMiXV0gPT0gVFJVRQogIHJsZV9sZW5ndGhzIDwtIHJsZV9yZXN1bHRbWyJsZW5ndGhzIl1dCiAgdHJ1ZV9zZXF1ZW50aWFscyA8LSBybGVfbGVuZ3Roc1tybGVfdmFsdWVzXQogIHJsZV9pZHggPC0gY3Vtc3VtKHJsZV9sZW5ndGhzKVt3aGljaChybGVfdmFsdWVzKV0KCiAgcG9zaXRpb25fdGFibGVbWyJsYXN0X3NlcXVlbnRpYWwiXV0gPC0gMAogIGNvdW50IDwtIDAKICBmb3IgKHIgaW4gcmxlX2lkeCkgewogICAgY291bnQgPC0gY291bnQgKyAxCiAgICBwb3NpdGlvbl90YWJsZVtyLCAibGFzdF9zZXF1ZW50aWFsIl0gPC0gdHJ1ZV9zZXF1ZW50aWFsc1tjb3VudF0KICB9CiAgbWVzc2FnZSgiVGhlIG1heGltdW0gc2VxdWVudGlhbCBzZXQgaXM6ICIsIG1heChwb3NpdGlvbl90YWJsZVtbImxhc3Rfc2VxdWVudGlhbCJdXSksICIuIikKCiAgd2FudGVkX2lkeCA8LSBwb3NpdGlvbl90YWJsZVtbImxhc3Rfc2VxdWVudGlhbCJdXSA+PSBtaW5pbXVtCiAgd2FudGVkIDwtIHBvc2l0aW9uX3RhYmxlW3dhbnRlZF9pZHgsIGMoImNociIsICJwb3MiKV0KICByZXR1cm4od2FudGVkKQp9Cgp6eW1vMjJfc2VxdWVudGlhbHMgPC0gc2VxdWVudGlhbF92YXJpYW50cyhuZXdfc2V0cywgY29uZGl0aW9ucyA9ICJ6MjIiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBtaW5pbXVtID0gMSwgbWF4aW11bV9zZXBhcmF0aW9uID0gMikKZGltKHp5bW8yMl9zZXF1ZW50aWFscykKIyMgNyBjYW5kaWRhdGUgcmVnaW9ucyBmb3Igenltb2RlbWUgMi4yIC0tIHRodXMgSSBhbSBiZXR0aW5nIHRoYXQgdGhlIHJlZmVyZW5jZSBzdHJhaW4gaXMgYSAyLjIKenltbzIzX3NlcXVlbnRpYWxzIDwtIHNlcXVlbnRpYWxfdmFyaWFudHMobmV3X3NldHMsIGNvbmRpdGlvbnMgPSAiejIzIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbWluaW11bSA9IDIsIG1heGltdW1fc2VwYXJhdGlvbiA9IDIpCmRpbSh6eW1vMjNfc2VxdWVudGlhbHMpCiMjIEluIGNvbnRyYXN0LCB0aGVyZSBhcmUgbG90cyAoNTg3KSBvZiBpbnRlcmVzdGluZyByZWdpb25zIGZvciAyLjMhCmBgYAoKIyMjIEV4dHJhY3QgYSBwcm9taXNpbmcgcmVnaW9uIGZyb20gdGhlIGdlbm9tZQoKVGhlIGZpcnN0IDQgY2FuZGlkYXRlIHJlZ2lvbnMgZnJvbSBteSBzZXQgb2YgcmVtYWluaW5nOgoqIENociAgICAgICBQb3MuICAgRGlzdGFuY2UKKiBMcGFMMTMtMTUgMjM4NDMzIDQ0OAoqIExwYUwxMy0xOCAxNDI4NDQgNjEzCiogTHBhTDEzLTI5IDgzMDM0MiAyNTIKKiBMcGFMMTMtMzMgMTMzMTUwNyA4NDMKCkxldHMgZGVmaW5lIGEgY291cGxlIG9mIHRlcm1zOgoqIFRoaXJkOiBFYWNoIG9mIHRoZSA0IGFib3ZlIHBvc2l0aW9ucy4KKiBTZWNvbmQ6IFRoaXJkIC0gRGlzdGFuY2UKKiBFbmQ6IFRoaXJkICsgUHJpbWVyTGVuCiogU3RhcnQ6IFNlY29uZCAtIFByaW1lcmxlbgoKSW4gZWFjaCBpbnN0YW5jZSwgdGhlc2UgYXJlIHRoZSBsYXN0IHBvc2l0aW9ucywgc28gd2Ugd2FudCB0byBncmFiIHRocmVlIHRoaW5nczoKCiogVGhlIGVudGlyZSByZWdpb24gZnJvbSBFbmQgLT4gU3RhcnQsIHRoaXMgd2F5IHdlIGNhbiBoYXZlIGEgcXVpY2sgc2FuaXR5IGNoZWNrLgoqIFN0YXJ0IC0+IFNlY29uZC4KKiAoVGhpcmQgLT4gRW5kKSA8LSBSZXZlcnNlIGNvbXBsZW1lbnRlZAoKYGBge3IgZXh0cmFjdF9ic2dlbm9tZSwgZXZhbD1GQUxTRX0KIyMgKiBMcGFMMTMtMTUgMjM4NDMzIDQ0OApmaXJzdF9jYW5kaWRhdGVfY2hyIDwtIGxwX2dlbm9tZVtbIkxwYUwxM18xNSJdXQpwcmltZXJfbGVuZ3RoIDwtIDIyCmFtcGxpY29uX2xlbmd0aCA8LSA0NDgKZmlyc3RfY2FuZGlkYXRlX3RoaXJkIDwtIDIzODQzMwpmaXJzdF9jYW5kaWRhdGVfc2Vjb25kIDwtIGZpcnN0X2NhbmRpZGF0ZV90aGlyZCAtIGFtcGxpY29uX2xlbmd0aApmaXJzdF9jYW5kaWRhdGVfc3RhcnQgPC0gZmlyc3RfY2FuZGlkYXRlX3NlY29uZCAtIHByaW1lcl9sZW5ndGgKZmlyc3RfY2FuZGlkYXRlX2VuZCA8LSBmaXJzdF9jYW5kaWRhdGVfdGhpcmQgKyBwcmltZXJfbGVuZ3RoCmZpcnN0X2NhbmRpZGF0ZV9yZWdpb24gPC0gc3Vic2VxKGZpcnN0X2NhbmRpZGF0ZV9jaHIsIGZpcnN0X2NhbmRpZGF0ZV9zdGFydCwgZmlyc3RfY2FuZGlkYXRlX2VuZCkKZmlyc3RfY2FuZGlkYXRlX3JlZ2lvbgpmaXJzdF9jYW5kaWRhdGVfNXAgPC0gc3Vic2VxKGZpcnN0X2NhbmRpZGF0ZV9jaHIsIGZpcnN0X2NhbmRpZGF0ZV9zdGFydCwgZmlyc3RfY2FuZGlkYXRlX3NlY29uZCkKYXMuY2hhcmFjdGVyKGZpcnN0X2NhbmRpZGF0ZV81cCkKZmlyc3RfY2FuZGlkYXRlXzNwIDwtIHNwZ3M6OnJldmVyc2VDb21wbGVtZW50KHN1YnNlcShmaXJzdF9jYW5kaWRhdGVfY2hyLCBmaXJzdF9jYW5kaWRhdGVfdGhpcmQsIGZpcnN0X2NhbmRpZGF0ZV9lbmQpKQpmaXJzdF9jYW5kaWRhdGVfM3AKCiMjICogTHBhTDEzLTE4IDE0Mjg0NCA2MTMKc2Vjb25kX2NhbmRpZGF0ZV9jaHIgPC0gbHBfZ2Vub21lW1siTHBhTDEzXzE4Il1dCnByaW1lcl9sZW5ndGggPC0gMjIKYW1wbGljb25fbGVuZ3RoIDwtIDYxMwpzZWNvbmRfY2FuZGlkYXRlX3RoaXJkIDwtIDE0Mjg0NApzZWNvbmRfY2FuZGlkYXRlX3NlY29uZCA8LSBzZWNvbmRfY2FuZGlkYXRlX3RoaXJkIC0gYW1wbGljb25fbGVuZ3RoCnNlY29uZF9jYW5kaWRhdGVfc3RhcnQgPC0gc2Vjb25kX2NhbmRpZGF0ZV9zZWNvbmQgLSBwcmltZXJfbGVuZ3RoCnNlY29uZF9jYW5kaWRhdGVfZW5kIDwtIHNlY29uZF9jYW5kaWRhdGVfdGhpcmQgKyBwcmltZXJfbGVuZ3RoCnNlY29uZF9jYW5kaWRhdGVfcmVnaW9uIDwtIHN1YnNlcShzZWNvbmRfY2FuZGlkYXRlX2Nociwgc2Vjb25kX2NhbmRpZGF0ZV9zdGFydCwgc2Vjb25kX2NhbmRpZGF0ZV9lbmQpCnNlY29uZF9jYW5kaWRhdGVfcmVnaW9uCnNlY29uZF9jYW5kaWRhdGVfNXAgPC0gc3Vic2VxKHNlY29uZF9jYW5kaWRhdGVfY2hyLCBzZWNvbmRfY2FuZGlkYXRlX3N0YXJ0LCBzZWNvbmRfY2FuZGlkYXRlX3NlY29uZCkKYXMuY2hhcmFjdGVyKHNlY29uZF9jYW5kaWRhdGVfNXApCnNlY29uZF9jYW5kaWRhdGVfM3AgPC0gc3Bnczo6cmV2ZXJzZUNvbXBsZW1lbnQoc3Vic2VxKHNlY29uZF9jYW5kaWRhdGVfY2hyLCBzZWNvbmRfY2FuZGlkYXRlX3RoaXJkLCBzZWNvbmRfY2FuZGlkYXRlX2VuZCkpCnNlY29uZF9jYW5kaWRhdGVfM3AKCgojIyAqIExwYUwxMy0yOSA4MzAzNDIgMjUyCnRoaXJkX2NhbmRpZGF0ZV9jaHIgPC0gbHBfZ2Vub21lW1siTHBhTDEzXzI5Il1dCnByaW1lcl9sZW5ndGggPC0gMjIKYW1wbGljb25fbGVuZ3RoIDwtIDI1Mgp0aGlyZF9jYW5kaWRhdGVfdGhpcmQgPC0gODMwMzQyCnRoaXJkX2NhbmRpZGF0ZV9zZWNvbmQgPC0gdGhpcmRfY2FuZGlkYXRlX3RoaXJkIC0gYW1wbGljb25fbGVuZ3RoCnRoaXJkX2NhbmRpZGF0ZV9zdGFydCA8LSB0aGlyZF9jYW5kaWRhdGVfc2Vjb25kIC0gcHJpbWVyX2xlbmd0aAp0aGlyZF9jYW5kaWRhdGVfZW5kIDwtIHRoaXJkX2NhbmRpZGF0ZV90aGlyZCArIHByaW1lcl9sZW5ndGgKdGhpcmRfY2FuZGlkYXRlX3JlZ2lvbiA8LSBzdWJzZXEodGhpcmRfY2FuZGlkYXRlX2NociwgdGhpcmRfY2FuZGlkYXRlX3N0YXJ0LCB0aGlyZF9jYW5kaWRhdGVfZW5kKQp0aGlyZF9jYW5kaWRhdGVfcmVnaW9uCnRoaXJkX2NhbmRpZGF0ZV81cCA8LSBzdWJzZXEodGhpcmRfY2FuZGlkYXRlX2NociwgdGhpcmRfY2FuZGlkYXRlX3N0YXJ0LCB0aGlyZF9jYW5kaWRhdGVfc2Vjb25kKQphcy5jaGFyYWN0ZXIodGhpcmRfY2FuZGlkYXRlXzVwKQp0aGlyZF9jYW5kaWRhdGVfM3AgPC0gc3Bnczo6cmV2ZXJzZUNvbXBsZW1lbnQoc3Vic2VxKHRoaXJkX2NhbmRpZGF0ZV9jaHIsIHRoaXJkX2NhbmRpZGF0ZV90aGlyZCwgdGhpcmRfY2FuZGlkYXRlX2VuZCkpCnRoaXJkX2NhbmRpZGF0ZV8zcAojIyBZb3UgYXJlIGEgZ2FyYmFnZSBwb2x5cHlyaW1pZGluZSB0cmFjdC4KIyMgV2hpY2ggaXMgYWN0dWFsbHkgaW50ZXJlc3RpbmcgaWYgdGhlIG11dGF0aW9ucyBtZXNzIGl0IHVwLgoKCiMjICogTHBhTDEzLTMzIDEzMzE1MDcgODQzCmZvdXJ0aF9jYW5kaWRhdGVfY2hyIDwtIGxwX2dlbm9tZVtbIkxwYUwxM18zMyJdXQpwcmltZXJfbGVuZ3RoIDwtIDIyCmFtcGxpY29uX2xlbmd0aCA8LSA4NDMKZm91cnRoX2NhbmRpZGF0ZV90aGlyZCA8LSAxMzMxNTA3CmZvdXJ0aF9jYW5kaWRhdGVfc2Vjb25kIDwtIGZvdXJ0aF9jYW5kaWRhdGVfdGhpcmQgLSBhbXBsaWNvbl9sZW5ndGgKZm91cnRoX2NhbmRpZGF0ZV9zdGFydCA8LSBmb3VydGhfY2FuZGlkYXRlX3NlY29uZCAtIHByaW1lcl9sZW5ndGgKZm91cnRoX2NhbmRpZGF0ZV9lbmQgPC0gZm91cnRoX2NhbmRpZGF0ZV90aGlyZCArIHByaW1lcl9sZW5ndGgKZm91cnRoX2NhbmRpZGF0ZV9yZWdpb24gPC0gc3Vic2VxKGZvdXJ0aF9jYW5kaWRhdGVfY2hyLCBmb3VydGhfY2FuZGlkYXRlX3N0YXJ0LCBmb3VydGhfY2FuZGlkYXRlX2VuZCkKZm91cnRoX2NhbmRpZGF0ZV9yZWdpb24KZm91cnRoX2NhbmRpZGF0ZV81cCA8LSBzdWJzZXEoZm91cnRoX2NhbmRpZGF0ZV9jaHIsIGZvdXJ0aF9jYW5kaWRhdGVfc3RhcnQsIGZvdXJ0aF9jYW5kaWRhdGVfc2Vjb25kKQphcy5jaGFyYWN0ZXIoZm91cnRoX2NhbmRpZGF0ZV81cCkKZm91cnRoX2NhbmRpZGF0ZV8zcCA8LSBzcGdzOjpyZXZlcnNlQ29tcGxlbWVudChzdWJzZXEoZm91cnRoX2NhbmRpZGF0ZV9jaHIsIGZvdXJ0aF9jYW5kaWRhdGVfdGhpcmQsIGZvdXJ0aF9jYW5kaWRhdGVfZW5kKSkKZm91cnRoX2NhbmRpZGF0ZV8zcApgYGAKCiMjIEdvIGh1bnRpbmcgZm9yIFNhbmdlciBzZXF1ZW5jaW5nIHJlZ2lvbnMKCkkgbWFkZSBhIGZ1biBsaXR0bGUgZnVuY3Rpb24gd2hpY2ggc2hvdWxkIGZpbmQgcmVnaW9ucyB3aGljaCBoYXZlIGxvdHMgb2YgdmFyaWFudHMKYXNzb2NpYXRlZCB3aXRoIGEgZ2l2ZW4gZXhwZXJpbWVudGFsIGZhY3Rvci4KCmBgYHtyfQpwaGVubyA8LSBzdWJzZXRfc2UobHBfc2UsIHN1YnNldCA9ICJjb25kaXRpb249PSd6Mi4yJ3xjb25kaXRpb249PSd6Mi4zJyIpCnBoZW5vIDwtIHN1YnNldF9zZShwaGVubywgc3Vic2V0ID0gIiFpcy5uYShjb2xEYXRhKHBoZW5vKVtbJ2JjZnRhYmxlJ11dKSIpCnBoZW5vX3NucHMgPC0gY291bnRfc25wcyhwaGVubywgYW5ub3RfY29sdW1uID0gImZyZWViYXllc3N1bW1hcnkiLCBzbnBfY29sdW1uPSJQQUlSRUQiKQojI3BoZW5vX3NucHMgPC0gc20oY291bnRfc25wcyhwaGVubywgYW5ub3RfY29sdW1uID0gImJjZnRhYmxlIikpCmBgYAoKIyMgU05QIERlbnNpdHkgUHJpbWVycwoKSSBjYW5ub3QgcnVuIHRoZSBmb2xsb3dpbmcgYmxvY2sgaW4gdGhlIGNvbnRhaW5lciB1bmxlc3MvdW50aWwgSSBjb3B5CnRoZSBnZmYgaW50byBpdC4uLgoKYGBge3IsIGV2YWw9RkFMU0V9CmZ1bl9zdHVmZiA8LSBzbnBfZGVuc2l0eV9wcmltZXJzKAogIHBoZW5vX3NucHMsCiAgYnNnZW5vbWUgPSAiQlNHZW5vbWUuTGVpc2htYW5pYS5wYW5hbWVuc2lzLk1IT01DT0w4MUwxMy52NTMiLAogIGdmZiA9ICJyZWZlcmVuY2UvVHJpVHJ5cERCLTUzX0xwYW5hbWVuc2lzTUhPTUNPTDgxTDEzLmdmZiIpCmRyb3Bfc2NhZmZvbGRzIDwtIGdyZXBsKHggPSByb3duYW1lcyhmdW5fc3R1ZmYkZmF2b3JpdGVzKSwgcGF0dGVybiA9ICJTQ0FGIikKZmF2b3JpdGVfcHJpbWVyX3JlZ2lvbnMgPC0gZnVuX3N0dWZmW1siZmF2b3JpdGVzIl1dWyFkcm9wX3NjYWZmb2xkcywgXQpmYXZvcml0ZV9wcmltZXJfcmVnaW9uc1tbImJpbiJdXSA8LSByb3duYW1lcyhmYXZvcml0ZV9wcmltZXJfcmVnaW9ucykKCmZhdm9yaXRlX3ByaW1lcl9yZWdpb25zIDwtIGZhdm9yaXRlX3ByaW1lcl9yZWdpb25zICU+JQogIHJlbG9jYXRlKGJpbikKYGBgCgojIyBDb21iaW5lIHRoaXMgdGFibGUgd2l0aCAyLjIvMi4zIGdlbmVzCgpIZXJlIGlzIG15IG5vdGUgZnJvbSBvdXIgbWVldGluZzoKCkNyb3NzIHJlZmVyZW5jZSBwcmltZXJzIHRvIERFIGdlbmVzIG9mIDIuMi8yLjMgYW5kL29yIHJlc2lzdGFuY2Uvc3VzY3BldGlibGUsCmFkZCBhIGNvbHVtbiB0byB0aGUgcHJpbWVyIHNwcmVhZHNoZWV0IHdpdGggdGhlIERFIGdlbmVzIChpbiByZXRyb3NwZWN0IEkgYW0gZ3Vlc3NpbmcKdGhpcyBhY3R1YWxseSBtZWFucyB0byBwdXQgdGhlIGxvZ0ZDIGFzIGEgY29sdW1uLgoKT25lIG5pY2UgdGhpbmcsIEkgZGlkIGEgc2VtYW50aWMgcmVtb3ZhbCBvbiB0aGUgbHBfc2UsIHNvIHRoZSBzZXQgb2YgbG9nRkMvcHZhbHVlcwpzaG91bGQgbm90IGhhdmUgYW55IG9mIHRoZSBvZmZlbmRpbmcgdHlwZXM7IHRodXMgSSBzaG91bGQgYmUgYWJsZSB0byBhdXRvbWFnaWNhbGx5CmdldCByaWQgb2YgdGhlbSBpbiB0aGUgbWVyZ2UuCgpUaGlzIGJsb2NrIG5lZWRzIHRvIGdvIGFmdGVyIGRpZmZlcmVudGlhbCBleHByZXNzaW9uIGFuYWx5c2VzLgoKYGBge3IsIGV2YWw9RkFMU0V9CmxvZ2ZjIDwtIHp5X3RhYmxlX3N2YVtbImRhdGEiXV1bWyJ6MjNfdnNfejIyIl1dCmxvZ2ZjX2NvbHVtbnMgPC0gbG9nZmNbLCBjKCJkZXNlcV9sb2dmYyIsICJkZXNlcV9hZGpwIildCmNvbG5hbWVzKGxvZ2ZjX2NvbHVtbnMpIDwtIGMoInoyM19sb2dmYyIsICJ6MjNfYWRqcCIpCm5ld190YWJsZSA8LSBtZXJnZShmYXZvcml0ZV9wcmltZXJfcmVnaW9ucywgbG9nZmNfY29sdW1ucywKICAgICAgICAgICAgICAgICAgIGJ5LnggPSAiY2xvc2VzdF9nZW5lX2JlZm9yZV9pZCIsIGJ5LnkgPSAicm93Lm5hbWVzIikKc3VzIDwtIHN1c190YWJsZV9zdmFbWyJkYXRhIl1dW1sic2Vuc2l0aXZlX3ZzX3Jlc2lzdGFudCJdXQpzdXNfY29sdW1ucyA8LSBzdXNbLCBjKCJkZXNlcV9sb2dmYyIsICJkZXNlcV9hZGpwIildCmNvbG5hbWVzKHN1c19jb2x1bW5zKSA8LSBjKCJzdXNfbG9nZmMiLCAic3VzX2FkanAiKQpuZXdfdGFibGUgPC0gbWVyZ2UobmV3X3RhYmxlLCBzdXNfY29sdW1ucywKICAgICAgICAgICAgICAgICAgIGJ5LnggPSAiY2xvc2VzdF9nZW5lX2JlZm9yZV9pZCIsIGJ5LnkgPSAicm93Lm5hbWVzIikgJT4lCiAgcmVsb2NhdGUoYmluKQp3cml0dGVuIDwtIHdyaXRlX3hsc3goZGF0YSA9IG5ld190YWJsZSwKICAgICAgICAgICAgICAgICAgICAgIGV4Y2VsID0gImV4Y2VsL2Zhdm9yaXRlX3ByaW1lcnNfeHJlZl96eV9zdXMueGxzeCIpCmBgYAoKCiMjIE1ha2UgYSBoZWF0bWFwIGRlc2NyaWJpbmcgdGhlIGNsdXN0ZXJpbmcgb2YgdmFyaWFudHMKCldlIGNhbiBjcm9zcyByZWZlcmVuY2UgdGhlIHZhcmlhbnRzIGFnYWluc3QgdGhlIHp5bW9kZW1lIHN0YXR1cyBhbmQKcGxvdCBhIGhlYXRtYXAgb2YgdGhlIHJlc3VsdHMgYW5kIGhvcGVmdWxseSBzZWUgaG93IHRoZXkgc2VwYXJhdGUuCgpgYGB7cn0Kc25wX2dlbmVzIDwtIHNtKHNucHNfdnNfZ2VuZXMobHBfc2UsIG5ld19zZXRzLCBjaHJfY29sdW1uID0gZXhwX2Nocl9jb2wsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHN0YXJ0X2NvbHVtbiA9IGV4cF9zdGFydF9jb2wsIGVuZF9jb2x1bW4gPSBleHBfZW5kX2NvbCkpCgpjbGluaWNhbF9jb2xvcnNfdjIgPC0gbGlzdCgKICAiejIyIiA9ICIjMDAwMGNjIiwKICAiejIzIiA9ICIjY2MwMDAwIikKbmV3X3p5bW9fbm9ybSA8LSBub3JtYWxpemVfc2UocHJ1bmVkX3NucHMsIG5vcm0gPSAicXVhbnQiKSAlPiUKICBzZXRfY29uZGl0aW9ucyhmYWN0ID0gInp5bW9kZW1lY2F0ZWdvcmljYWwiLCBjb2xvcnMgPSBjbGluaWNhbF9jb2xvcnNfdjIpCiMgIHNldF9zZV9jb2xvcnMoY2xpbmljYWxfY29sb3JzX3YyKQoKenltb19oZWF0IDwtIHBsb3RfZGlzaGVhdChuZXdfenltb19ub3JtKQpkZXYgPC0gcHAoZmlsZSA9ICJpbWFnZXMvb25seXoyMl96MjNfc25wX2hlYXRtYXAucGRmIiwgd2lkdGggPSAxMiwgaGVpZ2h0ID0gMTIpCnp5bW9faGVhdFtbInBsb3QiXV0KY2xvc2VkIDwtIGRldi5vZmYoKQp6eW1vX2hlYXQKYGBgCgojIyMgQW5ub3RhdGVkIGhlYXRtYXAgb2YgdmFyaWFudHMKCk5vdyBsZXQgdXMgdHJ5IHRvIG1ha2UgYSBoZWF0bWFwIHdoaWNoIGluY2x1ZGVzIHNvbWUgb2YgdGhlIGFubm90YXRpb24gZGF0YS4KCmBgYHtyfQpkZXMgPC0gY29sRGF0YShib3RoX25vcm0pCnVuZGVmX2lkeCA8LSBpcy5uYShkZXNbWyJwYXRob2dlbnN0cmFpbiJdXSkKZGVzW3VuZGVmX2lkeCwgInBhdGhvZ2Vuc3RyYWluIl0gPC0gInVua25vd24iCgojI2htY29scyA8LSBjb2xvclJhbXBQYWxldHRlKGMoInllbGxvdyIsImJsYWNrIiwiZGFya2JsdWUiKSkoMjU2KQpjb3JyZWxhdGlvbnMgPC0gaHBnbF9jb3IoYXNzYXkoYm90aF9ub3JtKSkKbmFfaWR4IDwtIGlzLm5hKGNvcnJlbGF0aW9ucykKY29ycmVsYXRpb25zW25hX2lkeF0gPC0gMAoKIyMgTWFrZSBhbiBpbml0aWFsIGhlYXRtYXAgdmlhIHBsb3RfZGlzaGVhdCwgd2hpY2ggbWF5IGdldCB1c2VkIGFzIHRoZSBmaWd1cmU6CmluaXRpYWxfc25wcyA8LSBzZXRfY29uZGl0aW9ucyhib3RoX25vcm0sIGZhY3QgPSAienltb2RlbWVyZWZlcmVuY2UiLCBjb2xvcnMgPSBjb2xvcl9jaG9pY2VzW1sic3RyYWluIl1dKQppbml0aWFsX2Rpc2hlYXQgPC0gcGxvdF9kaXNoZWF0KGJvdGhfbm9ybSkKZGV2IDwtIHBwKGZpbGUgPSAiZmlndXJlcy9pbml0aWFsX3NucF9oZWF0bWFwLnBkZiIsIHdpZHRoID0gMjAsIGhlaWdodCA9IDIwKQppbml0aWFsX2Rpc2hlYXRbWyJwbG90Il1dCmNsb3NlZCA8LSBkZXYub2ZmKCkKenltb19oZWF0Cgp6eW1vX21pc3NpbmdfaWR4IDwtIGlzLm5hKGRlc1tbInp5bW9kZW1lY2F0ZWdvcmljYWwiXV0pCmRlc1tbInp5bW9kZW1lY2F0ZWdvcmljYWwiXV0gPC0gYXMuY2hhcmFjdGVyKGRlc1tbInp5bW9kZW1lY2F0ZWdvcmljYWwiXV0pCmRlc1tbImNsaW5pY2FsY2F0ZWdvcmljYWwiXV0gPC0gYXMuY2hhcmFjdGVyKGRlc1tbImNsaW5pY2FsY2F0ZWdvcmljYWwiXV0pCmRlc1t6eW1vX21pc3NpbmdfaWR4LCAienltb2RlbWVjYXRlZ29yaWNhbCJdIDwtICJ1bmtub3duIgpteWRlbmRybyA8LSBsaXN0KAogICJjbHVzdGZ1biIgPSBoY2x1c3QsCiAgImx3ZCIgPSAyLjApCmNvbF9kYXRhIDwtIGFzLmRhdGEuZnJhbWUoZGVzWywgYygienltb2RlbWVjYXRlZ29yaWNhbCIpXSkKdW5rbm93bl9jbGluaWNhbCA8LSBpcy5uYShkZXNbWyJjbGluaWNhbGNhdGVnb3JpY2FsIl1dKQpjb2xuYW1lcyhjb2xfZGF0YSkgPC0gYygienltb2RlbWUiKQoKcm93X2RhdGEgPC0gYXMuZGF0YS5mcmFtZShkZXNbLCBjKCJzdXNfY2F0ZWdvcnlfY3VycmVudCIsICJjbGluaWNhbGNhdGVnb3JpY2FsIildKQpjb2xuYW1lcyhyb3dfZGF0YSkgPC0gYygic3VzY2VwdGliaWxpdHkiLCAib3V0Y29tZSIpCnJvd19kYXRhW3Vua25vd25fY2xpbmljYWwsICJvdXRjb21lIl0gPC0gInVuZGVmaW5lZCIKCm15YW5ub3QgPC0gbGlzdCgKICAiQ29sIiA9IGxpc3QoImRhdGEiID0gY29sX2RhdGEpLAogICJSb3ciID0gbGlzdCgiZGF0YSIgPSByb3dfZGF0YSkpCm15Y2x1c3QgPC0gbGlzdCgiY3V0aCIgPSAxLjAsCiAgICAgICAgICAgICAgICAiY29sIiA9IEJyZXdlckNsdXN0ZXJDb2wpCm15bGFicyA8LSBsaXN0KAogICJSb3ciID0gbGlzdCgibnJvdyIgPSA0KSwKICAiQ29sIiA9IGxpc3QoIm5yb3ciID0gNCkpCmhtY29scyA8LSBjb2xvclJhbXBQYWxldHRlKGMoImRhcmtibHVlIiwgImJlaWdlIikpKDI0MCkKenltb19hbm5vdF9oZWF0IDwtIGFubkhlYXRtYXAyKAogIGNvcnJlbGF0aW9ucywKICBkZW5kcm9ncmFtID0gbXlkZW5kcm8sCiAgYW5ub3RhdGlvbiA9IG15YW5ub3QsCiAgY2x1c3RlciA9IG15Y2x1c3QsCiAgbGFiZWxzID0gbXlsYWJzLAogICMjIFRoZSBmb2xsb3dpbmcgY29udHJvbHMgaWYgdGhlIHBpY3R1cmUgaXMgc3ltbWV0cmljCiAgc2NhbGUgPSAibm9uZSIsCiAgY29sID0gaG1jb2xzKQoKZGV2IDwtIHBwKGZpbGUgPSAiaW1hZ2VzL2RlbmRyb19oZWF0bWFwLnBkZiIsIGhlaWdodCA9IDIwLCB3aWR0aCA9IDIwKQpwbG90KHp5bW9fYW5ub3RfaGVhdCkKY2xvc2VkIDwtIGRldi5vZmYoKQpwbG90KHp5bW9fYW5ub3RfaGVhdCkKYGBgCgpQcmludCB0aGUgbGFyZ2VyIGhlYXRtYXAgc28gdGhhdCBhbGwgdGhlIGxhYmVscyBhcHBlYXIuICBLZWVwIGluIG1pbmQKdGhhdCBhcyB3ZSBnZXQgbW9yZSBzYW1wbGVzLCB0aGlzIGltYWdlIG5lZWRzIHRvIGNvbnRpbnVlIGdldHRpbmcKYmlnZ2VyLgoKIyMjIENNcGxvdCBrYXJ5b2dyYW0gb2YgdmFyaWFudHMKCkkgY2Fubm90IHJ1biB0aGUgZm9sbG93aW5nIGJsb2NrIHVudGlsL3VubGVzcyBJIGluc3RhbGwgY21wbG90IGluIHRoZQpjb250YWluZXIuICBPaCwgSSBkaWQhICBMZXQgdXMgcnVuIGl0IGFuZCBzZWUgd2hhdCBoYXBwZW5zLgoKYGBge3J9CnhyZWZfcHJvcCA8LSB0YWJsZShjb2xEYXRhKHBoZW5vX3NucHMpW1siY29uZGl0aW9uIl1dKQp4cmVmX3Byb3AKaWR4X3RibCA8LSBhc3NheShwaGVub19zbnBzKSA+IDUKbmV3X3RibCA8LSBkYXRhLmZyYW1lKHJvdy5uYW1lcyA9IHJvd25hbWVzKGFzc2F5KHBoZW5vX3NucHMpKSkKZm9yIChuIGluIG5hbWVzKHhyZWZfcHJvcCkpIHsKICBzYW1wbGVzIDwtIGNvbERhdGEocGhlbm9fc25wcylbWyJjb25kaXRpb24iXV0gPT0gbgogIG5ld190YmxbW25dXSA8LSAwCiAgcHJvcF9jb2wgPC0gcm93U3VtcyhpZHhfdGJsWywgc2FtcGxlc10pIC8geHJlZl9wcm9wW25dCiAgbmV3X3RibFtuXSA8LSBwcm9wX2NvbAp9CmtlZXBlcnMgPC0gZ3JlcGwoeCA9IHJvd25hbWVzKG5ld190YmwpLCBwYXR0ZXJuID0gIkxwYUwxMyIpCm5ld190YmwgPC0gbmV3X3RibFtrZWVwZXJzLCBdCm5ld190YmxbWyJzdHJvbmcyMiJdXSA8LSAxLjAwMSAtIG5ld190YmxbWyJ6Mi4yIl1dCm5ld190YmxbWyJzdHJvbmcyMyJdXSA8LSAxLjAwMSAtIG5ld190YmxbWyJ6Mi4zIl1dCnMyMl9uYSA8LSBuZXdfdGJsW1sic3Ryb25nMjIiXV0gPiAxCm5ld190YmxbczIyX25hLCAic3Ryb25nMjIiXSA8LSAxCnMyM19uYSA8LSBuZXdfdGJsW1sic3Ryb25nMjMiXV0gPiAxCm5ld190YmxbczIzX25hLCAic3Ryb25nMjMiXSA8LSAxCgpuZXdfdGJsW1siU05QIl1dIDwtIHJvd25hbWVzKG5ld190YmwpCm5ld190YmxbWyJDaHJvbW9zb21lIl1dIDwtIGdzdWIoeCA9IG5ld190YmxbWyJTTlAiXV0sIHBhdHRlcm4gPSAiY2hyXyguKilfcG9zXy4qIiwgcmVwbGFjZW1lbnQgPSAiXFwxIikKbmV3X3RibFtbIlBvc2l0aW9uIl1dIDwtIGdzdWIoeCA9IG5ld190YmxbWyJTTlAiXV0sIHBhdHRlcm4gPSAiLipfcG9zXyhcXGQrKV8uKiIsIHJlcGxhY2VtZW50ID0gIlxcMSIpCm5ld190YmwgPC0gbmV3X3RibFssIGMoIlNOUCIsICJDaHJvbW9zb21lIiwgIlBvc2l0aW9uIiwgInN0cm9uZzIyIiwgInN0cm9uZzIzIildCgpzaW1wbGlmeSA8LSBuZXdfdGJsCnNpbXBsaWZ5W1sic3Ryb25nMjIiXV0gPC0gTlVMTAoKQ01wbG90KG5ld190YmwsIGJpbi5zaXplID0gMTAwMDAsIHRocmVzaG9sZCA9IGMoMC4wMSwgMC4wNSksIHBsb3QudHlwZSA9ICJkIiwKICAgICAgIGZpbGUubmFtZSA9ICJ2YXJpYW50X2RlbnNpdHlfMTBrIikKQ01wbG90KG5ld190YmwsIGJpbi5zaXplID0gMTAwMCwgdGhyZXNob2xkID0gYygwLjAxLCAwLjA1KSwgcGxvdC50eXBlID0gImQiLAogICAgICAgZmlsZS5uYW1lID0gInZhcmlhbnRfZGVuc2l0eV8xayIpCkNNcGxvdChuZXdfdGJsLCBiaW4uc2l6ZSA9IDEwMDAwMCwgdGhyZXNob2xkID0gYygwLjAxLCAwLjA1KSwgcGxvdC50eXBlID0gImQiLAogICAgICAgZmlsZS5uYW1lID0gInZhcmlhbnRfZGVuc2l0eV8xMDBrIikKCkNNcGxvdChuZXdfdGJsLCBwbG90LnR5cGUgPSAibSIsIG11bHRyYWNrcyA9IFRSVUUsIHRocmVzaG9sZCA9IGMoMC4wMSwgMC4wNSksCiAgICAgICB0aHJlc2hvbGQubHdkID0gYygxLDEpLCB0aHJlc2hvbGQuY29sID0gYygiYmxhY2siLCJncmV5IiksCiAgICAgICBhbXBsaWZ5ID0gVFJVRSwgYmluLnNpemUgPSAxMDAwLAogICAgICAgY2hyLmRlbi5jb2wgPSBjKCJkYXJrZ3JlZW4iLCAieWVsbG93IiwgInJlZCIpLAogICAgICAgc2lnbmFsLmNvbCA9IGMoInJlZCIsICJncmVlbiIsICJibHVlIiksCiAgICAgICBzaWduYWwuY2V4ID0gMSwgZmlsZSA9ICJqcGciLCBkcGkgPSAzMDAsIGZpbGUub3V0cHV0ID0gVFJVRSwgdmVyYm9zZSA9IFRSVUUpCmBgYAoKIVtTTlAgRGVuc2l0eV0oTWFya2VyX0RlbnNpdHkudmFyaWFudF9kZW5zaXR5XzEway5qcGcpCgojIEEgZGlmZmVyZW50IGthcnlvZ3JhbQoKSSBoYXZlIGJlZW4gYSBiaXQgZnJ1c3RyYXRlZCB3aXRoIHRoZSBjbHVua3luZXNzIG9mIGNtcGxvdCwgc28gSSBkaWQKc29tZSByZWFkaW5nIGFuZCBmb3VuZCBhdXRvcGxvdC4gIEl0IG1ha2VzIHVzZSBvZiBnL2lyYW5nZXMgdG8gcGxvdAphcmJpdHJhcnkgZGF0YSBhbmQgYXMgc3VjaCBoYXMgdGhlIHBvdGVudGlhbCB0byBiZSBzaWduaWZpY2FudGx5IG1vcmUKZ2VuZXJhbGx5IHVzZWZ1bCB0aGFuIGNtcGxvdC4gIEkgdGhpbmsgSSB3aWxsIGJlIGFibGUgdG8gdXNlIGl0IHRvCnZpZXcgYSBsb3Qgb2YgaW50ZXJlc3RpbmcgZGlmZmVyZW50IGRhdGEgdHlwZXMuICBJbiB0aGlzIGluc3RhbmNlIEkKd2FudCB0byBwbG90IGRlbnNpdHkgb2YgdmFyaWFudHMgYXNzb2NpYXRlZCB3aXRoIHZhcmlvdXMgY29uZGl0aW9ucyBpbgp0aGUgZGF0YSAoejIuMy96Mi4yLCBjdXJlL2ZhaWwsIHdoYXRldmVyKS4gIEluIGFkZGl0aW9uLCBpdCBtaWdodCBiZQpuaWNlIHRvIGhhdmUgdGhlIE9SRnMgZGlzcGxheWVkIGluIHNvbWUgZmFzaGlvbiAoc3BhY2UgcGVybWl0dGluZykuCgpJIGFtIHByZXR0eSBzdXJlIEkgbWFkZSBhIGZ1bmN0aW9uIHdoaWNoIG1ha2VzIHRoaXMgbGVzcyBjbHVua3kgdGhhbiB3aGF0IGZvbGxvd3MuCgpgYGB7ciwgZXZhbD1GQUxTRX0KbHBfZW50cnkgPC0gRXVQYXRoREI6OmdldF9ldXBhdGhfZW50cnkoc3BlY2llcyA9ICJNSE9NL0NPTCIsIG1ldGFkYXRhID0gZXVfbWV0YSkKCiMjIFRoZXNlIGxpbmVzIGNhbm5vdCBydW4gaW4gdGhlIGNvbnRhaW5lciBiZWNhdXNlIGl0IGNhbm5vdCB3cml0ZQojI3R4ZGJfcGtnbmFtZSA8LSBtYWtlX2V1cGF0aF90eGRiKGxwX2VudHJ5KQojI2dyYW5nZV9uYW1lIDwtIG1ha2VfZXVwYXRoX2dyYW5nZXMobHBfZW50cnkpCmdyYW5nZV9uYW1lIDwtIGdzdWIoeCA9IGxwX2VudHJ5W1siR3Jhbmdlc1BrZyJdXSwgcGF0dGVybiA9ICJcXC5yZGEkIiwgcmVwbGFjZW1lbnQgPSAiIikKZ3JhbmdlX2ZpbGVuYW1lIDwtIGZpbGUucGF0aCgiYnVpbGQiLCBscF9lbnRyeVtbIkdyYW5nZXNQa2ciXV0pCmlmIChmaWxlLmV4aXN0cyhncmFuZ2VfZmlsZW5hbWUpKSB7CiAgbG9hZChncmFuZ2VfZmlsZW5hbWUpCn0gZWxzZSB7CiAgY3JlYXRlZCA8LSBkaXIuY3JlYXRlKCJidWlsZC9nZmYiLCByZWN1cnNpdmUgPSBUUlVFKQogIGdyYW5nZV9idWlsZCA8LSBtYWtlX2V1cGF0aF9ncmFuZ2VzKGxwX2VudHJ5KQogIGdyYW5nZV9maWxlbmFtZSA8LSBncmFuZ2VfYnVpbGRbWyJyZGEiXV0KICBsb2FkKGdyYW5nZV9maWxlbmFtZSkKfQpncmFuZ2VfZGF0YSA8LSBnZXQwKGdyYW5nZV9uYW1lKQoKc2NhZmZvbGRfaWR4IDwtIGdyZXBsKHggPSBhcy5jaGFyYWN0ZXIoc2VxbmFtZXMoZ3JhbmdlX2RhdGEpKSwgcGF0dGVybiA9ICJTQ0FGIikKbm9fc2NhZmZvbGRzIDwtIGdyYW5nZV9kYXRhWyFzY2FmZm9sZF9pZHhdCnNjYWZmb2xkX2lkeCA8LSBncmVwbCh4ID0gYXMuY2hhcmFjdGVyKG5hbWVzKHNlcWluZm8oZ3JhbmdlX2RhdGEpKSksIHBhdHRlcm4gPSAiU0NBRiIpCmNocl9uYW1lcyA8LSBuYW1lcyhzZXFpbmZvKGdyYW5nZV9kYXRhKSlbIXNjYWZmb2xkX2lkeF0Kbm9fc2NhZmZvbGRzIDwtIHNlcWluZm8oZ3JhbmdlX2RhdGEpW2Nocl9uYW1lc10KCmF1dG9fdGJsIDwtIG5ld190YmwKYXV0b190YmxbWyJwb3NpdGlvbjIiXV0gPC0gYXV0b190YmxbWyJQb3NpdGlvbiJdXQphdXRvX3RibFtbIlNOUCJdXSA8LSBOVUxMCnJvd25hbWVzKGF1dG9fdGJsKSA8LSBOVUxMCgp0aWxlc2l6ZSA8LSAxMDAwCmJpbnNfMWsgPC0gR2Vub21pY1Jhbmdlczo6dGlsZUdlbm9tZShzZXFsZW5ndGhzKG5vX3NjYWZmb2xkcyksIHRpbGV3aWR0aCA9IDEwMDAsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjdXQubGFzdC50aWxlLmluLmNocm9tID0gVFJVRSkKYmluc181ayA8LSBHZW5vbWljUmFuZ2VzOjp0aWxlR2Vub21lKHNlcWxlbmd0aHMobm9fc2NhZmZvbGRzKSwgdGlsZXdpZHRoID0gNTAwMCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGN1dC5sYXN0LnRpbGUuaW4uY2hyb20gPSBUUlVFKQpiaW5zXzEwayA8LSBHZW5vbWljUmFuZ2VzOjp0aWxlR2Vub21lKHNlcWxlbmd0aHMobm9fc2NhZmZvbGRzKSwgdGlsZXdpZHRoID0gMTAwMDAsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjdXQubGFzdC50aWxlLmluLmNocm9tID0gVFJVRSkKYmluc18xbnQgPC0gR2Vub21pY1Jhbmdlczo6dGlsZUdlbm9tZShzZXFsZW5ndGhzKG5vX3NjYWZmb2xkcyksIHRpbGV3aWR0aCA9IDEsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY3V0Lmxhc3QudGlsZS5pbi5jaHJvbSA9IFRSVUUpCmF1dG9fdGJsW1sic3RyYW5kIl1dIDwtICIrIgojIyBJIHdhbnQgdG8gY2FsY3VsYXRlIHRoZSBudW1iZXIgb2YgaW50ZXJzZWN0aW5nIHBvc2l0aW9ucyBiZXR3ZWVuIG15IGF1dG9fdGJsIGFuZCB0aGUgMWsgYmlucy4Kc3RhcnQgPC0gYXV0b190YmxbLCBjKCJDaHJvbW9zb21lIiwgIlBvc2l0aW9uIiwgInBvc2l0aW9uMiIsICJzdHJhbmQiLCAic3Ryb25nMjMiKV0KY29sbmFtZXMoc3RhcnQpIDwtIGMoImNociIsICJzdGFydCIsICJlbmQiLCAic3RyYW5kIiwgInoyMyIpCnN0YXJ0W1siY2hyIl1dIDwtIGdzdWIoeCA9IHN0YXJ0W1siY2hyIl1dLCBwYXR0ZXJuID0gIi0iLCByZXBsYWNlbWVudCA9ICJfIikKdmFyX2dyYW5nZSA8LSBtYWtlR1Jhbmdlc0Zyb21EYXRhRnJhbWUoc3RhcnQsIHNlcWluZm8gPSBub19zY2FmZm9sZHMsIGtlZXAuZXh0cmEuY29sdW1ucyA9IFRSVUUpCnZhcnNfcGVyX2JpbiA8LSBmaW5kT3ZlcmxhcHMoYmluc18xaywgdmFyX2dyYW5nZSkKdmFyc19wZXJfYmluX251bWVyaWMgPC0gYXMuZGF0YS5mcmFtZShiaW5zXzFrKQp2YXJzX3Blcl9iaW5fbnVtZXJpY1tbImJpbiJdXSA8LSByb3duYW1lcyh2YXJzX3Blcl9iaW5fbnVtZXJpYykKCmNvdW50X3Blcl9iaW4gPC0gYXMuZGF0YS5mcmFtZSh2YXJzX3Blcl9iaW4pICU+JQogIGdyb3VwX2J5KHF1ZXJ5SGl0cykgJT4lCiAgZHBseXI6OnRhbGx5KCkKY29sbmFtZXMoY291bnRfcGVyX2JpbikgPC0gYygiYmluIiwgIm51bSIpCnZhcnNfcGVyX2Jpbl9udW1lcmljIDwtIG1lcmdlKHZhcnNfcGVyX2Jpbl9udW1lcmljLCBjb3VudF9wZXJfYmluLCBieSA9ICJiaW4iLCBhbGwueCA9IFRSVUUpCm1pc3NpbmdfaWR4IDwtIGlzLm5hKHZhcnNfcGVyX2Jpbl9udW1lcmljW1sibnVtIl1dKQp2YXJzX3Blcl9iaW5fbnVtZXJpY1ttaXNzaW5nX2lkeCwgIm51bSJdIDwtIDAKdmFyc19wZXJfYmluIDwtIHZhcnNfcGVyX2Jpbl9udW1lcmljWywgYygic2VxbmFtZXMiLCAic3RhcnQiLCAiZW5kIiwgIndpZHRoIiwgInN0cmFuZCIsICJudW0iKV0KdnBiX2dyYW5nZSA8LSBtYWtlR1Jhbmdlc0Zyb21EYXRhRnJhbWUodmFyc19wZXJfYmluLCBzZXFpbmZvID0gbm9fc2NhZmZvbGRzLCBrZWVwLmV4dHJhLmNvbHVtbnMgPSBUUlVFKQoKa2FyeSA8LSBhdXRvcGxvdCh2cGJfZ3JhbmdlLCBsYXlvdXQgPSAia2FyeW9ncmFtIiwgYWVzKGNvbG9yID0gbnVtLCBmaWxsID0gbnVtKSkgKwogIHNjYWxlX2NvbG9yX2dyYWRpZW50KGxvdyA9ICJibHVlIiwgaGlnaCA9ICJyZWQiKSArCiAgc2NhbGVfZmlsbF9ncmFkaWVudChsb3cgPSAiYmx1ZSIsIGhpZ2ggPSAicmVkIikKIyMgdGhlbWVfYncoYmFzZV9zaXplID0gMTApICsKcHAoZmlsZSA9ICJrYXJ5b2dyYW1fYnlfdmFyaWFudHMucGRmIiwgaGVpZ2h0ID0gMjQsIHdpZHRoID0gMTgpCmthcnkKZGV2Lm9mZigpCgp2YXJfa2FyeSA8LSBnZ2JpbygpICsKICBsYXlvdXRfa2FyeW9ncmFtKHZwYl9ncmFuZ2UsIGFlcyhjb2xvciA9IG51bSwgZmlsbCA9IG51bSkpICsKICBzY2FsZV9maWxsX2dyYWRpZW50KGxvdyA9ICJibHVlIiwgaGlnaCA9ICJ3aGl0ZSIpICsKICBzY2FsZV9jb2xvcl9ncmFkaWVudChsb3cgPSAiYmx1ZSIsIGhpZ2ggPSAid2hpdGUiKSArCiAgdGhlbWVfYncoYmFzZV9zaXplID0gMTApCnZhcl9rYXJ5CmBgYAoKPCEtLS0KIVtTTlAgRGVuc2l0eV0oU05QLURlbnNpdHkucmF0aW8uanBnKQohW0NpcmN1bGFyIE1hbmhhdHRhbl0oQ2lyY3VsYXItTWFuaGF0dGFuLnJhdGlvLmpwZykKIVtSZWN0YW5ndWxhciBNYW5oYXR0YW5dKFJlY3Rhbmd1bGFyLU1hbmhhdHRhbi5yYXRpby5qcGcpCiFbUVFdKFFRcGxvdC5yYXRpby5qcGcpCi0tLT4KCiMjIFRyeSBvdXQgTWF0cml4RVFUTAoKVGhpcyB0b29sIGxvb2tzIGEgbGl0dGxlIG9wYXF1ZSwgYnV0IHByb3ZpZGVzIHNhbXBsZSBkYXRhIHdpdGggdGhpbmdzCnRoYXQgbWFrZSBzZW5zZSB0byBtZSBhbmQgc2hvdWxkIGJlIHByZXR0eSBlYXN5IHRvIHJlY2FwaXR1bGF0ZSBpbiBvdXIKZGF0YS4KCjEuICBjb3ZhcmlhdGVzLnR4dDogQ29sdW1ucyBhcmUgc2FtcGxlcywgcm93cyBhcmUgdGhpbmdzIGZyb20gY29sRGF0YSAtLSB0aGUKbW9zdCBsaWtlbHkgb25lcyBvZiBpbnRlcmVzdCBmb3Igb3VyIGRhdGEgd291bGQgYmUgenltb2RlbWUsCnNlbnNpdGl2aXR5CjIuICBnZW5lbG9jLnR4dDogY29sdW1ucyBhcmUgJ2dlbmVpZCcsICdjaHInLCAnbGVmdCcsICdyaWdodCcuICBJCmd1ZXNzIEkgY2FuIGFzc3VtZSBsZWZ0IGFuZCByaWdodCBhcmUgc3RhcnQvc3RvcDsgaW4gd2hpY2ggY2FzZQp0aGlzIGlzIHRyaXZpYWxseSBhY3F1aXJhYmxlIGZyb20gcm93RGF0YS4KMy4gIGdlLnR4dDogVGhpcyBhcHBlYXJzIHRvIGJlIGEgbG9nKHJwa20vY3BtKSB0YWJsZSB3aXRoIHJvd3MgYXMgZ2VuZXMgYW5kCmNvbHVtbnMgYXMgc2FtcGxlcwo0LiAgc25wc2xvYy50eHQ6IGNvbHVtbnMgYXJlICdzbnBpZCcsICdjaHInLCAncG9zJwo1LiAgc25wcy50eHQ6IGNvbHVtbnMgYXJlIHNhbXBsZXMsIHJvd3MgYXJlIHRoZSBpZHMgZnJvbSBzbnNwbG9jLAp2YWx1ZXMgYSAwLDEsMi4gIEkgYXNzdW1lIDAgaXMgaWRlbnRpY2FsIGFuZCAxLi4xMiBhcmUgdGhlIHZhcmlvdXMKQS0+VEdDIFQtPkFHQyBDLT5BR1QgRy0+QUNUCgpgYGB7ciBtYXRyaXhlcXRsLCBldmFsPUZBTFNFfQojIyBGb3IgdGhpcywgbGV0IHVzIHVzZSB0aGUgJ25ld19zbnBzJyBkYXRhIHN0cnVjdHVyZS4KIyMgQ2F2ZWF0IGhlcmU6IHRoZXNlIG5lZWQgdG8gYmUgY29lcmNlZCB0byBudW1iZXJzLgpteV9jb3ZhcmlhdGVzIDwtIGNvbERhdGEobmV3X3NucHMpWywgYygienltb2RlbWVjYXRlZ29yaWNhbCIsICJjbGluaWNhbGNhdGVnb3JpY2FsIildCmZvciAoY29sIGluIGNvbG5hbWVzKG15X2NvdmFyaWF0ZXMpKSB7CiAgbXlfY292YXJpYXRlc1tbY29sXV0gPC0gYXMubnVtZXJpYyhhcy5mYWN0b3IobXlfY292YXJpYXRlc1tbY29sXV0pKQp9Cm15X2NvdmFyaWF0ZXMgPC0gdChteV9jb3ZhcmlhdGVzKQoKbXlfZ2VuZWxvYyA8LSByb3dEYXRhKGxwX3NlKVssIGMoImdpZCIsICJjaHJvbW9zb21lIiwgInN0YXJ0IiwgImVuZCIpXQpjb2xuYW1lcyhteV9nZW5lbG9jKSA8LSBjKCJnZW5laWQiLCAiY2hyIiwgImxlZnQiLCAicmlnaHQiKQoKbXlfZ2UgPC0gYXNzYXkobm9ybWFsaXplX3NlKGxwX3NlLCB0cmFuc2Zvcm0gPSAibG9nMiIsIGZpbHRlciA9IFRSVUUsIGNvbnZlcnQgPSAiY3BtIikpCnVzZWRfc2FtcGxlcyA8LSB0b2xvd2VyKGNvbG5hbWVzKG15X2dlKSkgJWluJSBjb2xuYW1lcyhhc3NheShuZXdfc25wcykpCm15X2dlIDwtIG15X2dlWywgdXNlZF9zYW1wbGVzXQoKbXlfc25wc2xvYyA8LSBkYXRhLmZyYW1lKHJvd25hbWVzID0gcm93bmFtZXMoYXNzYXkobmV3X3NucHMpKSkKIyMgT2gsIGNhdmVhdCBoZXJlOiBCZWNhdXNlIG9mIHRoZSB3YXkgSSBzdG9yZWQgdGhlIGRhdGEsCiMjIEkgY291bGQgaGF2ZSBkdXBsaWNhdGUgcm93cyB3aGljaCBwcmVzdW1hYmx5IHdpbGwgbWFrZSBtYXRyaXhFUVRMIHNhZApteV9zbnBzbG9jW1siY2hyIl1dIDwtIGdzdWIocGF0dGVybiA9ICJeY2hyXyguKylfcG9zKC4rKV9yZWZfLiokIiwgcmVwbGFjZW1lbnQgPSAiXFwxIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIHggPSByb3duYW1lcyhteV9zbnBzbG9jKSkKbXlfc25wc2xvY1tbInBvcyJdXSA8LSBnc3ViKHBhdHRlcm4gPSAiXmNocl8oLispX3BvcyguKylfcmVmXy4qJCIsIHJlcGxhY2VtZW50ID0gIlxcMiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICB4ID0gcm93bmFtZXMobXlfc25wc2xvYykpCnRlc3QgPC0gZHVwbGljYXRlZChteV9zbnBzbG9jKQojIyBFYWNoIGR1cGxpY2F0ZWQgcm93IHdvdWxkIGJlIGFub3RoZXIgdmFyaWFudCBhdCB0aGF0IHBvc2l0aW9uOwojIyBzbyBpbiB0aGVvcnkgd2Ugd291bGQgZG8gYSBybGUgdG8gbnVtYmVyIHRoZW0gSSBhbSBndWVzc2luZwojIyBIb3dldmVyLCBJIGRvIG5vdCBoYXZlIGRpZmZlcmVudCB2YXJpYW50cyBzbyBJIHRoaW5rIEkgY2FuIGlnbm9yZSB0aGlzIGZvciB0aGUgbW9tZW50CiMjIGJ1dCB3aWxsIG5lZWQgdG8gbWFrZSBteSBtYXRyaXggZWl0aGVyIDAgb3IgMS4KaWYgKHN1bSh0ZXN0KSA+IDApIHsKICBtZXNzYWdlKCJUaGVyZSBhcmU6ICIsIHN1bShkdXBsaWNhdGVkKSwgIiBkdXBsaWNhdGVkIGVudHJpZXMuIikKICBrZWVwX2lkeCA8LSAhIHRlc3QKICBteV9zbnBzbG9jIDwtIG15X3NucHNsb2Nba2VlcF9pZHgsIF0KfQoKbXlfc25wcyA8LSBhc3NheShuZXdfc25wcykKb25lX2lkeCA8LSBteV9zbnBzID4gMApteV9zbnBzW29uZV9pZHhdIDwtIDEKCiMjIE9rLCBhdCB0aGlzIHBvaW50IEkgdGhpbmsgSSBoYXZlIGFsbCB0aGUgcGllY2VzIHdoaWNoIHRoaXMgbWV0aG9kIHdhbnRzLi4uCiMjIE9oLCBubyBJIGd1ZXNzIG5vdDsgaXQgYWN0dWFsbHkgd2FudHMgdGhlIGRhdGEgYXMgYSBzZXQgb2YgZmlsZW5hbWVzLi4uCmxpYnJhcnkoTWF0cml4RVFUTCkKd3JpdGUudGFibGUobXlfc25wcywgImVxdGwvc25wcy50c3YiLCBuYSA9ICJOQSIsIGNvbC5uYW1lcyA9IFRSVUUsIHJvdy5uYW1lcyA9IFRSVUUsIHNlcCA9ICJcdCIsIHF1b3RlID0gVFJVRSkKIyMgcmVhZHI6OndyaXRlX3RzdihteV9zbnBzLCAiZXF0bC9zbnBzLnRzdiIsICkKd3JpdGUudGFibGUobXlfc25wc2xvYywgImVxdGwvc25wc2xvYy50c3YiLCBuYSA9ICJOQSIsIGNvbC5uYW1lcyA9IFRSVUUsIHJvdy5uYW1lcyA9IFRSVUUsIHNlcCA9ICJcdCIsIHF1b3RlID0gVFJVRSkKIyMgcmVhZHI6OndyaXRlX3RzdihteV9zbnBzbG9jLCAiZXF0bC9zbnBzbG9jLnRzdiIpCndyaXRlLnRhYmxlKGFzLmRhdGEuZnJhbWUobXlfZ2UpLCAiZXF0bC9nZS50c3YiLCBuYSA9ICJOQSIsIGNvbC5uYW1lcyA9IFRSVUUsIHJvdy5uYW1lcyA9IFRSVUUsIHNlcCA9ICJcdCIsIHF1b3RlID0gVFJVRSkKIyMgcmVhZHI6OndyaXRlX3Rzdihhcy5kYXRhLmZyYW1lKG15X2dlKSwgImVxdGwvZ2UudHN2IikKd3JpdGUudGFibGUoYXMuZGF0YS5mcmFtZShteV9nZW5lbG9jKSwgImVxdGwvZ2VuZWxvYy50c3YiLCBuYSA9ICJOQSIsIGNvbC5uYW1lcyA9IFRSVUUsIHJvdy5uYW1lcyA9IFRSVUUsIHNlcCA9ICJcdCIsIHF1b3RlID0gVFJVRSkKIyMgcmVhZHI6OndyaXRlX3Rzdihhcy5kYXRhLmZyYW1lKG15X2dlbmVsb2MpLCAiZXF0bC9nZW5lbG9jLnRzdiIpCndyaXRlLnRhYmxlKGFzLmRhdGEuZnJhbWUobXlfY292YXJpYXRlcyksICJlcXRsL2NvdmFyaWF0ZXMudHN2IiwgbmEgPSAiTkEiLCBjb2wubmFtZXMgPSBUUlVFLCByb3cubmFtZXMgPSBUUlVFLCBzZXAgPSAiXHQiLCBxdW90ZSA9IFRSVUUpCiMjIHJlYWRyOjp3cml0ZV90c3YoYXMuZGF0YS5mcmFtZShteV9jb3ZhcmlhdGVzKSwgImVxdGwvY292YXJpYXRlcy50c3YiKQoKdXNlTW9kZWwgPSBtb2RlbExJTkVBUiAjIG1vZGVsQU5PVkEsIG1vZGVsTElORUFSLCBvciBtb2RlbExJTkVBUl9DUk9TUwoKIyBHZW5vdHlwZSBmaWxlIG5hbWUKU05QX2ZpbGVfbmFtZSA9ICJlcXRsL3NucHMudHN2IgpzbnBzX2xvY2F0aW9uX2ZpbGVfbmFtZSA9ICJlcXRsL3NucHNsb2MudHN2IgpleHByZXNzaW9uX2ZpbGVfbmFtZSA9ICJlcXRsL2dlLnRzdiIKZ2VuZV9sb2NhdGlvbl9maWxlX25hbWUgPSAiZXF0bC9nZW5lbG9jLnRzdiIKY292YXJpYXRlc19maWxlX25hbWUgPSAiZXF0bC9jb3ZhcmlhdGVzLnRzdiIKIyBPdXRwdXQgZmlsZSBuYW1lCm91dHB1dF9maWxlX25hbWVfY2lzID0gdGVtcGZpbGUoKQpvdXRwdXRfZmlsZV9uYW1lX3RyYSA9IHRlbXBmaWxlKCkKIyBPbmx5IGFzc29jaWF0aW9ucyBzaWduaWZpY2FudCBhdCB0aGlzIGxldmVsIHdpbGwgYmUgc2F2ZWQKcHZPdXRwdXRUaHJlc2hvbGRfY2lzID0gMC4xCnB2T3V0cHV0VGhyZXNob2xkX3RyYSA9IDAuMQojIEVycm9yIGNvdmFyaWFuY2UgbWF0cml4CiMgU2V0IHRvIG51bWVyaWMoKSBmb3IgaWRlbnRpdHkuCmVycm9yQ292YXJpYW5jZSA9IG51bWVyaWMoKQojIGVycm9yQ292YXJpYW5jZSA9IHJlYWQudGFibGUoIlNhbXBsZV9EYXRhL2Vycm9yQ292YXJpYW5jZS50eHQiKTsKIyBEaXN0YW5jZSBmb3IgbG9jYWwgZ2VuZS1TTlAgcGFpcnMKY2lzRGlzdCA9IDFlNgojIyBMb2FkIGdlbm90eXBlIGRhdGEKc25wcyA9IFNsaWNlZERhdGEkbmV3KCkKc25wcyRmaWxlRGVsaW1pdGVyID0gIlx0IiAgICAgICMgdGhlIFRBQiBjaGFyYWN0ZXIKc25wcyRmaWxlT21pdENoYXJhY3RlcnMgPSAiTkEiICMgZGVub3RlIG1pc3NpbmcgdmFsdWVzOwpzbnBzJGZpbGVTa2lwUm93cyA9IDEgICAgICAgICAgIyBvbmUgcm93IG9mIGNvbHVtbiBsYWJlbHMKc25wcyRmaWxlU2tpcENvbHVtbnMgPSAxICAgICAgICMgb25lIGNvbHVtbiBvZiByb3cgbGFiZWxzCnNucHMkZmlsZVNsaWNlU2l6ZSA9IDIwMDAgICAgICAjIHJlYWQgZmlsZSBpbiBzbGljZXMgb2YgMiwwMDAgcm93cwpzbnBzJExvYWRGaWxlKFNOUF9maWxlX25hbWUpCiMjIExvYWQgZ2VuZSBleHByZXNzaW9uIGRhdGEKZ2VuZSA9IFNsaWNlZERhdGEkbmV3KCkKZ2VuZSRmaWxlRGVsaW1pdGVyID0gIlx0IiAgICAgICMgdGhlIFRBQiBjaGFyYWN0ZXIKZ2VuZSRmaWxlT21pdENoYXJhY3RlcnMgPSAiTkEiICMgZGVub3RlIG1pc3NpbmcgdmFsdWVzOwpnZW5lJGZpbGVTa2lwUm93cyA9IDEgICAgICAgICAgIyBvbmUgcm93IG9mIGNvbHVtbiBsYWJlbHMKZ2VuZSRmaWxlU2tpcENvbHVtbnMgPSAxICAgICAgICMgb25lIGNvbHVtbiBvZiByb3cgbGFiZWxzCmdlbmUkZmlsZVNsaWNlU2l6ZSA9IDIwMDAgICAgICAjIHJlYWQgZmlsZSBpbiBzbGljZXMgb2YgMiwwMDAgcm93cwpnZW5lJExvYWRGaWxlKGV4cHJlc3Npb25fZmlsZV9uYW1lKQojIyBMb2FkIGNvdmFyaWF0ZXMKY3ZydCA9IFNsaWNlZERhdGEkbmV3KCkKY3ZydCRmaWxlRGVsaW1pdGVyID0gIlx0IiAgICAgICMgdGhlIFRBQiBjaGFyYWN0ZXIKY3ZydCRmaWxlT21pdENoYXJhY3RlcnMgPSAiTkEiICMgZGVub3RlIG1pc3NpbmcgdmFsdWVzOwpjdnJ0JGZpbGVTa2lwUm93cyA9IDEgICAgICAgICAgIyBvbmUgcm93IG9mIGNvbHVtbiBsYWJlbHMKY3ZydCRmaWxlU2tpcENvbHVtbnMgPSAxICAgICAgICMgb25lIGNvbHVtbiBvZiByb3cgbGFiZWxzCmlmKGxlbmd0aChjb3ZhcmlhdGVzX2ZpbGVfbmFtZSkgPiAwKSB7CiAgY3ZydCRMb2FkRmlsZShjb3ZhcmlhdGVzX2ZpbGVfbmFtZSkKfQojIyBSdW4gdGhlIGFuYWx5c2lzCnNucHNwb3MgPSByZWFkLnRhYmxlKHNucHNfbG9jYXRpb25fZmlsZV9uYW1lLCBoZWFkZXIgPSBUUlVFLCBzdHJpbmdzQXNGYWN0b3JzID0gRkFMU0UpCmdlbmVwb3MgPSByZWFkLnRhYmxlKGdlbmVfbG9jYXRpb25fZmlsZV9uYW1lLCBoZWFkZXIgPSBUUlVFLCBzdHJpbmdzQXNGYWN0b3JzID0gRkFMU0UpCgptZSA9IE1hdHJpeF9lUVRMX21haW4oCiAgc25wcyA9IHNucHMsCiAgZ2VuZSA9IGdlbmUsCiAgY3ZydCA9IGN2cnQsCiAgb3V0cHV0X2ZpbGVfbmFtZSA9IG91dHB1dF9maWxlX25hbWVfdHJhLAogIHB2T3V0cHV0VGhyZXNob2xkID0gcHZPdXRwdXRUaHJlc2hvbGRfdHJhLAogIHVzZU1vZGVsID0gdXNlTW9kZWwsCiAgZXJyb3JDb3ZhcmlhbmNlID0gZXJyb3JDb3ZhcmlhbmNlLAogIHZlcmJvc2UgPSBUUlVFLAogIG91dHB1dF9maWxlX25hbWUuY2lzID0gb3V0cHV0X2ZpbGVfbmFtZV9jaXMsCiAgcHZPdXRwdXRUaHJlc2hvbGQuY2lzID0gcHZPdXRwdXRUaHJlc2hvbGRfY2lzLAogIHNucHNwb3MgPSBzbnBzcG9zLAogIGdlbmVwb3MgPSBnZW5lcG9zLAogIGNpc0Rpc3QgPSBjaXNEaXN0LAogIHB2YWx1ZS5oaXN0ID0gInFxcGxvdCIsCiAgbWluLnB2LmJ5LmdlbmVzbnAgPSBGQUxTRSwKICBub0ZEUnNhdmVNZW1vcnkgPSBGQUxTRSk7CmBgYAoKYGBge3Igc2F2ZW1lfQpwYW5kZXI6OnBhbmRlcihzZXNzaW9uSW5mbygpKQptZXNzYWdlKHBhc3RlMCgiVGhpcyBpcyBocGdsdG9vbHMgY29tbWl0OiAiLCBnZXRfZ2l0X2NvbW1pdCgpKSkKIyNtZXNzYWdlKHBhc3RlMCgiU2F2aW5nIHRvICIsIHNhdmVmaWxlKSkKIyMgdG1wIDwtIHNtKHNhdmVtZShmaWxlbmFtZSA9IHNhdmVmaWxlKSkKYGBgCgpgYGB7ciBsb2FkbWVfYWZ0ZXIsIGV2YWwgPSBGQUxTRX0KdG1wIDwtIGxvYW1lKGZpbGVuYW1lID0gc2F2ZWZpbGUpCmBgYAo=